diff --git a/src/.vuepress/config/sidebar-pt.js b/src/.vuepress/config/sidebar-pt.js index 8419cb1..d7d0af4 100644 --- a/src/.vuepress/config/sidebar-pt.js +++ b/src/.vuepress/config/sidebar-pt.js @@ -88,24 +88,36 @@ module.exports = [ "/exemplos/linguagem-v0.8.3/import.md", "/exemplos/linguagem-v0.8.3/biblioteca.md", "/exemplos/linguagem-v0.8.3/hashing-with-keccak256.md", - "/exemplos/linguagem-v0.8.3/verificando-assinatura.md" + "/exemplos/linguagem-v0.8.3/verificando-assinatura.md", ], }, { title: "Aplicações", path: "/exemplos/aplicacoes/", children: [ + "/exemplos/aplicacoes/carteira-ether.md", "/exemplos/aplicacoes/carteira-multi-assinaturas.md", "/exemplos/aplicacoes/arvore-de-merkle.md", "/exemplos/aplicacoes/mapping-iteravel", "/exemplos/aplicacoes/erc20.md", + "/exemplos/aplicacoes/erc721.md", + "/exemplos/aplicacoes/erc1155.md", + "/exemplos/aplicacoes/transferencia-de-token-sem-gas.md", + "/exemplos/aplicacoes/contrato-de-bytecode-simples.md", "/exemplos/aplicacoes/endereco-de-contrato-pre-computado-com-create2.md", "/exemplos/aplicacoes/contrato-proxy-minimo.md", + "/exemplos/aplicacoes/proxy-atualizavel.md", "/exemplos/aplicacoes/implante-qualquer-contrato.md", + "/exemplos/aplicacoes/gravar-em-qualquer-slot.md", "/exemplos/aplicacoes/canal-de-pagamento-unidirecional.md", "/exemplos/aplicacoes/canal-de-pagamento-bidirecional.md", "/exemplos/aplicacoes/leilao-ingles.md", "/exemplos/aplicacoes/leilao-holandes.md", + "/exemplos/aplicacoes/vaquinha.md", + "/exemplos/aplicacoes/multi-chamadas.md", + "/exemplos/aplicacoes/multi-delegatecall.md", + "/exemplos/aplicacoes/bloqueio-de-tempo.md", + "/exemplos/aplicacoes/exponenciacao-binaria-em-assembly.md", ], }, { @@ -153,7 +165,9 @@ module.exports = [ { title: "Patterns and Standards", path: "/evm-maquina-virtual-ethereum/patterns-and-standards", - children: ["/evm-maquina-virtual-ethereum/patterns-and-standards/erc20-and-eip-20.md"], + children: [ + "/evm-maquina-virtual-ethereum/patterns-and-standards/erc20-and-eip-20.md", + ], }, ], }, @@ -171,4 +185,4 @@ module.exports = [ }, ], }, -] +]; diff --git a/src/exemplos/aplicacoes/arvore-de-merkle.md b/src/exemplos/aplicacoes/arvore-de-merkle.md index 30888b7..855b623 100644 --- a/src/exemplos/aplicacoes/arvore-de-merkle.md +++ b/src/exemplos/aplicacoes/arvore-de-merkle.md @@ -1,12 +1,10 @@ # Árvore de Merkle -A árvore de Merkle te permite provar criptograficamente que um elemento está contido - -num conjunto sem revelar o conjunto inteiro. +A árvore de Merkle permite você provar criptograficamente que um elemento está contido num conjunto sem revelar o conjunto inteiro. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; contract MerkleProof { function verify( @@ -70,17 +68,21 @@ contract TestMerkleProof is MerkleProof { /* verifica 3rd leaf - 0x1bbd78ae6188015c4a6772eb1526292b5985fc3272ead4c65002240fb9ae5d13 + 0xdca3326ad7e8121bf9cf9c12333e6b2271abe823ec9edfe42f813b1e768fa57b - raiz - 0x074b43252ffb4a469154df5fb7fe4ecce30953ba8b7095fe1e006185f017ad10 + root + 0xcc086fcc038189b4641db2cc4f1de3bb132aefbd65d510d817591550937818c7 index 2 prova - 0x948f90037b4ea787c14540d9feb1034d4a5bc251b9b5f8e57d81e4b470027af8 - 0x63ac1b92046d474f84be3aa0ee04ffe5600862228c81803cce07ac40484aee43 + 0x8da9e1c820f9dbd1589fd6585872bc1063588625729e7ab0797cfc63a00bd950 + 0x995788ffc103b987ad50f5e5707fd094419eb12d9552cc423bd0cd86a3861433 */ } ``` + +## Teste no Remix + +- [MerkleProof.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IE1lcmtsZVByb29mIHsKICAgIGZ1bmN0aW9uIHZlcmlmeSgKICAgICAgICBieXRlczMyW10gbWVtb3J5IHByb29mLAogICAgICAgIGJ5dGVzMzIgcm9vdCwKICAgICAgICBieXRlczMyIGxlYWYsCiAgICAgICAgdWludCBpbmRleAogICAgKSBwdWJsaWMgcHVyZSByZXR1cm5zIChib29sKSB7CiAgICAgICAgYnl0ZXMzMiBoYXNoID0gbGVhZjsKCiAgICAgICAgZm9yICh1aW50IGkgPSAwOyBpIDwgcHJvb2YubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgYnl0ZXMzMiBwcm9vZkVsZW1lbnQgPSBwcm9vZltpXTsKCiAgICAgICAgICAgIGlmIChpbmRleCAlIDIgPT0gMCkgewogICAgICAgICAgICAgICAgaGFzaCA9IGtlY2NhazI1NihhYmkuZW5jb2RlUGFja2VkKGhhc2gsIHByb29mRWxlbWVudCkpOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgaGFzaCA9IGtlY2NhazI1NihhYmkuZW5jb2RlUGFja2VkKHByb29mRWxlbWVudCwgaGFzaCkpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBpbmRleCA9IGluZGV4IC8gMjsKICAgICAgICB9CgogICAgICAgIHJldHVybiBoYXNoID09IHJvb3Q7CiAgICB9Cn0KCmNvbnRyYWN0IFRlc3RNZXJrbGVQcm9vZiBpcyBNZXJrbGVQcm9vZiB7CiAgICBieXRlczMyW10gcHVibGljIGhhc2hlczsKCiAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgICBzdHJpbmdbNF0gbWVtb3J5IHRyYW5zYWN0aW9ucyA9IFsKICAgICAgICAgICAiYWxpY2UgLT4gYm9iIiwKICAgICAgICAgICAiYm9iIC0+IGRhdmUiLAogICAgICAgICAgICJjYXJvbCAtPiBhbGljZSIsCiAgICAgICAgICAgImRhdmUgLT4gYm9iIgogICAgICAgIF07CgogICAgICAgIGZvciAodWludCBpID0gMDsgaSA8IHRyYW5zYWN0aW9ucy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICBoYXNoZXMucHVzaChrZWNjYWsyNTYoYWJpLmVuY29kZVBhY2tlZCh0cmFuc2FjdGlvbnNbaV0pKSk7CiAgICAgICAgfQoKICAgICAgICB1aW50IG4gPSB0cmFuc2FjdGlvbnMubGVuZ3RoOwogICAgICAgIHVpbnQgb2Zmc2V0ID0gMDsKCiAgICAgICAgd2hpbGUgKG4gPiAwKSB7CiAgICAgICAgICAgIGZvciAodWludCBpID0gMDsgaSA8IG4gLSAxOyBpICs9IDIpIHsKICAgICAgICAgICAgICAgIGhhc2hlcy5wdXNoKAogICAgICAgICAgICAgICAgICAgIGtlY2NhazI1NigKICAgICAgICAgICAgICAgICAgICAgICAgYWJpLmVuY29kZVBhY2tlZChoYXNoZXNbb2Zmc2V0ICsgaV0sIGhhc2hlc1tvZmZzZXQgKyBpICsgMV0pCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBvZmZzZXQgKz0gbjsKICAgICAgICAgICAgbiA9IG4gLyAyOwogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBnZXRSb290KCkgcHVibGljIHZpZXcgcmV0dXJucyAoYnl0ZXMzMikgewogICAgICAgIHJldHVybiBoYXNoZXNbaGFzaGVzLmxlbmd0aCAtIDFdOwogICAgfQoKICAgIC8qIHZlcmlmaWNhCiAgICAzcmQgbGVhZgogICAgMHhkY2EzMzI2YWQ3ZTgxMjFiZjljZjljMTIzMzNlNmIyMjcxYWJlODIzZWM5ZWRmZTQyZjgxM2IxZTc2OGZhNTdiCgogICAgcm9vdAogICAgMHhjYzA4NmZjYzAzODE4OWI0NjQxZGIyY2M0ZjFkZTNiYjEzMmFlZmJkNjVkNTEwZDgxNzU5MTU1MDkzNzgxOGM3CgogICAgaW5kZXgKICAgIDIKCiAgICBwcm92YQogICAgMHg4ZGE5ZTFjODIwZjlkYmQxNTg5ZmQ2NTg1ODcyYmMxMDYzNTg4NjI1NzI5ZTdhYjA3OTdjZmM2M2EwMGJkOTUwCiAgICAweDk5NTc4OGZmYzEwM2I5ODdhZDUwZjVlNTcwN2ZkMDk0NDE5ZWIxMmQ5NTUyY2M0MjNiZDBjZDg2YTM4NjE0MzMKICAgICovCn0=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/bloqueio-de-tempo.md b/src/exemplos/aplicacoes/bloqueio-de-tempo.md new file mode 100644 index 0000000..e7a50d8 --- /dev/null +++ b/src/exemplos/aplicacoes/bloqueio-de-tempo.md @@ -0,0 +1,158 @@ +# Bloqueio de tempo + +`TimeLock` é um contrato que publica uma transação a ser executada no futuro. Após um período de espera mínimo, a transação pode ser executada. + +`TimeLocks` são comumente usados ​​em DAOs. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract TimeLock { + error NotOwnerError(); + error AlreadyQueuedError(bytes32 txId); + error TimestampNotInRangeError(uint blockTimestamp, uint timestamp); + error NotQueuedError(bytes32 txId); + error TimestampNotPassedError(uint blockTimestmap, uint timestamp); + error TimestampExpiredError(uint blockTimestamp, uint expiresAt); + error TxFailedError(); + + event Queue( + bytes32 indexed txId, + address indexed target, + uint value, + string func, + bytes data, + uint timestamp + ); + event Execute( + bytes32 indexed txId, + address indexed target, + uint value, + string func, + bytes data, + uint timestamp + ); + event Cancel(bytes32 indexed txId); + + uint public constant MIN_DELAY = 10; // segundos + uint public constant MAX_DELAY = 1000; // segundos + uint public constant GRACE_PERIOD = 1000; // segundos + + address public owner; + // tx id => queued + mapping(bytes32 => bool) public queued; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + if (msg.sender != owner) { + revert NotOwnerError(); + } + _; + } + + receive() external payable {} + + function getTxId( + address _target, + uint _value, + string calldata _func, + bytes calldata _data, + uint _timestamp + ) public pure returns (bytes32) { + return keccak256(abi.encode(_target, _value, _func, _data, _timestamp)); + } + + /** + * @param _target Address of contract or account to call + * @param _value Amount of ETH to send + * @param _func Function signature, for example "foo(address,uint256)" + * @param _data ABI encoded data send. + * @param _timestamp Timestamp after which the transaction can be executed. + */ + function queue( + address _target, + uint _value, + string calldata _func, + bytes calldata _data, + uint _timestamp + ) external onlyOwner returns (bytes32 txId) { + txId = getTxId(_target, _value, _func, _data, _timestamp); + if (queued[txId]) { + revert AlreadyQueuedError(txId); + } + // ---|------------|---------------|------- + // block block + min block + max + if ( + _timestamp < block.timestamp + MIN_DELAY || + _timestamp > block.timestamp + MAX_DELAY + ) { + revert TimestampNotInRangeError(block.timestamp, _timestamp); + } + + queued[txId] = true; + + emit Queue(txId, _target, _value, _func, _data, _timestamp); + } + + function execute( + address _target, + uint _value, + string calldata _func, + bytes calldata _data, + uint _timestamp + ) external payable onlyOwner returns (bytes memory) { + bytes32 txId = getTxId(_target, _value, _func, _data, _timestamp); + if (!queued[txId]) { + revert NotQueuedError(txId); + } + // ----|-------------------|------- + // timestamp timestamp + período de carência + if (block.timestamp < _timestamp) { + revert TimestampNotPassedError(block.timestamp, _timestamp); + } + if (block.timestamp > _timestamp + GRACE_PERIOD) { + revert TimestampExpiredError(block.timestamp, _timestamp + GRACE_PERIOD); + } + + queued[txId] = false; + + // preparar dados + bytes memory data; + if (bytes(_func).length > 0) { + // data = func selector + _data + data = abi.encodePacked(bytes4(keccak256(bytes(_func))), _data); + } else { + // chamada de fallback com dados + data = _data; + } + + // alvo de chamada + (bool ok, bytes memory res) = _target.call{value: _value}(data); + if (!ok) { + revert TxFailedError(); + } + + emit Execute(txId, _target, _value, _func, _data, _timestamp); + + return res; + } + + function cancel(bytes32 _txId) external onlyOwner { + if (!queued[_txId]) { + revert NotQueuedError(_txId); + } + + queued[_txId] = false; + + emit Cancel(_txId); + } +} +``` + +## Teste no Remix + +- [TimeLock.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IFRpbWVMb2NrIHsKICAgIGVycm9yIE5vdE93bmVyRXJyb3IoKTsKICAgIGVycm9yIEFscmVhZHlRdWV1ZWRFcnJvcihieXRlczMyIHR4SWQpOwogICAgZXJyb3IgVGltZXN0YW1wTm90SW5SYW5nZUVycm9yKHVpbnQgYmxvY2tUaW1lc3RhbXAsIHVpbnQgdGltZXN0YW1wKTsKICAgIGVycm9yIE5vdFF1ZXVlZEVycm9yKGJ5dGVzMzIgdHhJZCk7CiAgICBlcnJvciBUaW1lc3RhbXBOb3RQYXNzZWRFcnJvcih1aW50IGJsb2NrVGltZXN0bWFwLCB1aW50IHRpbWVzdGFtcCk7CiAgICBlcnJvciBUaW1lc3RhbXBFeHBpcmVkRXJyb3IodWludCBibG9ja1RpbWVzdGFtcCwgdWludCBleHBpcmVzQXQpOwogICAgZXJyb3IgVHhGYWlsZWRFcnJvcigpOwoKICAgIGV2ZW50IFF1ZXVlKAogICAgICAgIGJ5dGVzMzIgaW5kZXhlZCB0eElkLAogICAgICAgIGFkZHJlc3MgaW5kZXhlZCB0YXJnZXQsCiAgICAgICAgdWludCB2YWx1ZSwKICAgICAgICBzdHJpbmcgZnVuYywKICAgICAgICBieXRlcyBkYXRhLAogICAgICAgIHVpbnQgdGltZXN0YW1wCiAgICApOwogICAgZXZlbnQgRXhlY3V0ZSgKICAgICAgICBieXRlczMyIGluZGV4ZWQgdHhJZCwKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgdGFyZ2V0LAogICAgICAgIHVpbnQgdmFsdWUsCiAgICAgICAgc3RyaW5nIGZ1bmMsCiAgICAgICAgYnl0ZXMgZGF0YSwKICAgICAgICB1aW50IHRpbWVzdGFtcAogICAgKTsKICAgIGV2ZW50IENhbmNlbChieXRlczMyIGluZGV4ZWQgdHhJZCk7CgogICAgdWludCBwdWJsaWMgY29uc3RhbnQgTUlOX0RFTEFZID0gMTA7IC8vIHNlZ3VuZG9zCiAgICB1aW50IHB1YmxpYyBjb25zdGFudCBNQVhfREVMQVkgPSAxMDAwOyAvLyBzZWd1bmRvcwogICAgdWludCBwdWJsaWMgY29uc3RhbnQgR1JBQ0VfUEVSSU9EID0gMTAwMDsgLy8gc2VndW5kb3MKCiAgICBhZGRyZXNzIHB1YmxpYyBvd25lcjsKICAgIC8vIHR4IGlkID0+IHF1ZXVlZAogICAgbWFwcGluZyhieXRlczMyID0+IGJvb2wpIHB1YmxpYyBxdWV1ZWQ7CgogICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgb3duZXIgPSBtc2cuc2VuZGVyOwogICAgfQoKICAgIG1vZGlmaWVyIG9ubHlPd25lcigpIHsKICAgICAgICBpZiAobXNnLnNlbmRlciAhPSBvd25lcikgewogICAgICAgICAgICByZXZlcnQgTm90T3duZXJFcnJvcigpOwogICAgICAgIH0KICAgICAgICBfOwogICAgfQoKICAgIHJlY2VpdmUoKSBleHRlcm5hbCBwYXlhYmxlIHt9CgogICAgZnVuY3Rpb24gZ2V0VHhJZCgKICAgICAgICBhZGRyZXNzIF90YXJnZXQsCiAgICAgICAgdWludCBfdmFsdWUsCiAgICAgICAgc3RyaW5nIGNhbGxkYXRhIF9mdW5jLAogICAgICAgIGJ5dGVzIGNhbGxkYXRhIF9kYXRhLAogICAgICAgIHVpbnQgX3RpbWVzdGFtcAogICAgKSBwdWJsaWMgcHVyZSByZXR1cm5zIChieXRlczMyKSB7CiAgICAgICAgcmV0dXJuIGtlY2NhazI1NihhYmkuZW5jb2RlKF90YXJnZXQsIF92YWx1ZSwgX2Z1bmMsIF9kYXRhLCBfdGltZXN0YW1wKSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBAcGFyYW0gX3RhcmdldCBBZGRyZXNzIG9mIGNvbnRyYWN0IG9yIGFjY291bnQgdG8gY2FsbAogICAgICogQHBhcmFtIF92YWx1ZSBBbW91bnQgb2YgRVRIIHRvIHNlbmQKICAgICAqIEBwYXJhbSBfZnVuYyBGdW5jdGlvbiBzaWduYXR1cmUsIGZvciBleGFtcGxlICJmb28oYWRkcmVzcyx1aW50MjU2KSIKICAgICAqIEBwYXJhbSBfZGF0YSBBQkkgZW5jb2RlZCBkYXRhIHNlbmQuCiAgICAgKiBAcGFyYW0gX3RpbWVzdGFtcCBUaW1lc3RhbXAgYWZ0ZXIgd2hpY2ggdGhlIHRyYW5zYWN0aW9uIGNhbiBiZSBleGVjdXRlZC4KICAgICAqLwogICAgZnVuY3Rpb24gcXVldWUoCiAgICAgICAgYWRkcmVzcyBfdGFyZ2V0LAogICAgICAgIHVpbnQgX3ZhbHVlLAogICAgICAgIHN0cmluZyBjYWxsZGF0YSBfZnVuYywKICAgICAgICBieXRlcyBjYWxsZGF0YSBfZGF0YSwKICAgICAgICB1aW50IF90aW1lc3RhbXAKICAgICkgZXh0ZXJuYWwgb25seU93bmVyIHJldHVybnMgKGJ5dGVzMzIgdHhJZCkgewogICAgICAgIHR4SWQgPSBnZXRUeElkKF90YXJnZXQsIF92YWx1ZSwgX2Z1bmMsIF9kYXRhLCBfdGltZXN0YW1wKTsKICAgICAgICBpZiAocXVldWVkW3R4SWRdKSB7CiAgICAgICAgICAgIHJldmVydCBBbHJlYWR5UXVldWVkRXJyb3IodHhJZCk7CiAgICAgICAgfQogICAgICAgIC8vIC0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0KICAgICAgICAvLyAgYmxvY2sgICAgYmxvY2sgKyBtaW4gICAgIGJsb2NrICsgbWF4CiAgICAgICAgaWYgKAogICAgICAgICAgICBfdGltZXN0YW1wIDwgYmxvY2sudGltZXN0YW1wICsgTUlOX0RFTEFZIHx8CiAgICAgICAgICAgIF90aW1lc3RhbXAgPiBibG9jay50aW1lc3RhbXAgKyBNQVhfREVMQVkKICAgICAgICApIHsKICAgICAgICAgICAgcmV2ZXJ0IFRpbWVzdGFtcE5vdEluUmFuZ2VFcnJvcihibG9jay50aW1lc3RhbXAsIF90aW1lc3RhbXApOwogICAgICAgIH0KCiAgICAgICAgcXVldWVkW3R4SWRdID0gdHJ1ZTsKCiAgICAgICAgZW1pdCBRdWV1ZSh0eElkLCBfdGFyZ2V0LCBfdmFsdWUsIF9mdW5jLCBfZGF0YSwgX3RpbWVzdGFtcCk7CiAgICB9CgogICAgZnVuY3Rpb24gZXhlY3V0ZSgKICAgICAgICBhZGRyZXNzIF90YXJnZXQsCiAgICAgICAgdWludCBfdmFsdWUsCiAgICAgICAgc3RyaW5nIGNhbGxkYXRhIF9mdW5jLAogICAgICAgIGJ5dGVzIGNhbGxkYXRhIF9kYXRhLAogICAgICAgIHVpbnQgX3RpbWVzdGFtcAogICAgKSBleHRlcm5hbCBwYXlhYmxlIG9ubHlPd25lciByZXR1cm5zIChieXRlcyBtZW1vcnkpIHsKICAgICAgICBieXRlczMyIHR4SWQgPSBnZXRUeElkKF90YXJnZXQsIF92YWx1ZSwgX2Z1bmMsIF9kYXRhLCBfdGltZXN0YW1wKTsKICAgICAgICBpZiAoIXF1ZXVlZFt0eElkXSkgewogICAgICAgICAgICByZXZlcnQgTm90UXVldWVkRXJyb3IodHhJZCk7CiAgICAgICAgfQogICAgICAgIC8vIC0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tCiAgICAgICAgLy8gIHRpbWVzdGFtcCAgICB0aW1lc3RhbXAgKyBwZXJpb2RvIGRlIGNhcmVuY2lhCiAgICAgICAgaWYgKGJsb2NrLnRpbWVzdGFtcCA8IF90aW1lc3RhbXApIHsKICAgICAgICAgICAgcmV2ZXJ0IFRpbWVzdGFtcE5vdFBhc3NlZEVycm9yKGJsb2NrLnRpbWVzdGFtcCwgX3RpbWVzdGFtcCk7CiAgICAgICAgfQogICAgICAgIGlmIChibG9jay50aW1lc3RhbXAgPiBfdGltZXN0YW1wICsgR1JBQ0VfUEVSSU9EKSB7CiAgICAgICAgICAgIHJldmVydCBUaW1lc3RhbXBFeHBpcmVkRXJyb3IoYmxvY2sudGltZXN0YW1wLCBfdGltZXN0YW1wICsgR1JBQ0VfUEVSSU9EKTsKICAgICAgICB9CgogICAgICAgIHF1ZXVlZFt0eElkXSA9IGZhbHNlOwoKICAgICAgICAvLyBwcmVwYXJhciBkYWRvcwogICAgICAgIGJ5dGVzIG1lbW9yeSBkYXRhOwogICAgICAgIGlmIChieXRlcyhfZnVuYykubGVuZ3RoID4gMCkgewogICAgICAgICAgICAvLyBkYXRhID0gZnVuYyBzZWxlY3RvciArIF9kYXRhCiAgICAgICAgICAgIGRhdGEgPSBhYmkuZW5jb2RlUGFja2VkKGJ5dGVzNChrZWNjYWsyNTYoYnl0ZXMoX2Z1bmMpKSksIF9kYXRhKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyBjaGFtYWRhIGRlIGZhbGxiYWNrIGNvbSBkYWRvcwogICAgICAgICAgICBkYXRhID0gX2RhdGE7CiAgICAgICAgfQoKICAgICAgICAvLyBhbHZvIGRlIGNoYW1hZGEKICAgICAgICAoYm9vbCBvaywgYnl0ZXMgbWVtb3J5IHJlcykgPSBfdGFyZ2V0LmNhbGx7dmFsdWU6IF92YWx1ZX0oZGF0YSk7CiAgICAgICAgaWYgKCFvaykgewogICAgICAgICAgICByZXZlcnQgVHhGYWlsZWRFcnJvcigpOwogICAgICAgIH0KCiAgICAgICAgZW1pdCBFeGVjdXRlKHR4SWQsIF90YXJnZXQsIF92YWx1ZSwgX2Z1bmMsIF9kYXRhLCBfdGltZXN0YW1wKTsKCiAgICAgICAgcmV0dXJuIHJlczsKICAgIH0KCiAgICBmdW5jdGlvbiBjYW5jZWwoYnl0ZXMzMiBfdHhJZCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICAgICAgICBpZiAoIXF1ZXVlZFtfdHhJZF0pIHsKICAgICAgICAgICAgcmV2ZXJ0IE5vdFF1ZXVlZEVycm9yKF90eElkKTsKICAgICAgICB9CgogICAgICAgIHF1ZXVlZFtfdHhJZF0gPSBmYWxzZTsKCiAgICAgICAgZW1pdCBDYW5jZWwoX3R4SWQpOwogICAgfQp9&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/canal-de-pagamento-bidirecional.md b/src/exemplos/aplicacoes/canal-de-pagamento-bidirecional.md index 200e693..0dec0bc 100644 --- a/src/exemplos/aplicacoes/canal-de-pagamento-bidirecional.md +++ b/src/exemplos/aplicacoes/canal-de-pagamento-bidirecional.md @@ -1,13 +1,12 @@ # Canal de Pagamento Bidirecional -Canais de pagamento bidirecional permite que os participantes `Alice` e `Bob` transfiram Ether off chain repetidamente. +Canais de pagamento bidirecional permite que os participantes `Alice` e `Bob` transfiram Ether off chain repetidamente. Pagamentos podem ser feitos em ambas direções, `Alice` paga `Bob` e `Bob` paga `Alice`. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; -pragma experimental ABIEncoderV2; +pragma solidity ^0.8.20; /* Abrindo um canal @@ -20,7 +19,7 @@ Atualizar saldos do canal 1. Repete os passos 1 - 3 da abertura do canal 2. Da carteira multi-sig cria uma transação que vai - apagar a transação que teria implementado o canal de pagamento antigo - - e depois cria a transação que pode implementar um canal de pagamento + - e depois cria a transação que pode implementar um canal de pagamento com novos balanços Fechando um canal quando Alice e Bob concordam com o saldo final @@ -34,11 +33,9 @@ Fechando um canal quando Alice e Bob não concordam com os saldos finais 3. Alice e Bob podem retirar fundos uma vez que o canal é extinto */ -import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.3/contracts/math/SafeMath.sol"; -import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.3/contracts/cryptography/ECDSA.sol"; +import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol"; contract BiDirectionalPaymentChannel { - using SafeMath for uint; using ECDSA for bytes32; event ChallengeExit(address indexed sender, uint nonce); @@ -55,7 +52,7 @@ contract BiDirectionalPaymentChannel { modifier checkBalances(uint[2] memory _balances) { require( - address(this).balance >= _balances[0].add(_balances[1]), + address(this).balance >= _balances[0] + _balances[1], "balance of contract must be >= to the total balance of users" ); _; @@ -115,7 +112,7 @@ contract BiDirectionalPaymentChannel { uint[2] memory _balances, uint _nonce ) { - // Nota: copia a matriz de armazenamento para a memória + // Note: copy storage array to memory address[2] memory signers; for (uint i = 0; i < users.length; i++) { signers[i] = users[i]; @@ -152,7 +149,7 @@ contract BiDirectionalPaymentChannel { } nonce = _nonce; - expiresAt = block.timestamp.add(challengePeriod); + expiresAt = block.timestamp + challengePeriod; emit ChallengeExit(msg.sender, nonce); } @@ -170,3 +167,7 @@ contract BiDirectionalPaymentChannel { } } ``` + +## Teste no Remix + +-[BiDirectionalPaymentChannel.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCi8qCkFicmluZG8gdW0gY2FuYWwKMS4gQWxpY2UgZSBCb2IgZGVwb3NpdGFtIGVtIHVtYSBjYXJ0ZWlyYSBtdWx0aS1zaWcKMi4gRW5kZXJlY28gZGUgY2FuYWwgZGUgcGFnYW1lbnRvIGUgcHJlLWNvbXB1dGFkbwozLiBBbGljZSBlIEJvYiB0cm9jYW0gYXNzaW5hdHVyYXMgZG9zIGJhbGFuY29zIGluaWNpYWlzCjQuIEFsaWNlIGUgQm9iIGNyaWFtIHVtYSB0cmFuc2FjYW8gcXVlIHBvZGUgaW1wbGVtZW50YXIgdW0gY2FuYWwgZGUgcGFnYW1lbnRvIGRhCmNhcnRlaXJhIG11bHRpLXNpZwpBdHVhbGl6YXIgc2FsZG9zIGRvIGNhbmFsCjEuIFJlcGV0ZSBvcyBwYXNzb3MgMSAtIDMgZGEgYWJlcnR1cmEgZG8gY2FuYWwKMi4gRGEgY2FydGVpcmEgbXVsdGktc2lnIGNyaWEgdW1hIHRyYW5zYWNhbyBxdWUgdmFpCiAgIC0gYXBhZ2FyIGEgdHJhbnNhY2FvIHF1ZSB0ZXJpYSBpbXBsZW1lbnRhZG8gbyBjYW5hbCBkZSBwYWdhbWVudG8gYW50aWdvCiAgIC0gZSBkZXBvaXMgY3JpYSBhIHRyYW5zYWNhbyBxdWUgcG9kZSBpbXBsZW1lbnRhciB1bSBjYW5hbCBkZSBwYWdhbWVudG8gCiAgICAgY29tIG5vdm9zIGJhbGFuY29zCgpGZWNoYW5kbyB1bSBjYW5hbCBxdWFuZG8gQWxpY2UgZSBCb2IgY29uY29yZGFtIGNvbSBvIHNhbGRvIGZpbmFsCjEuIERhIGNhcnRlaXJhIG11bHRpLXNpZyBjcmlhIHVtYSB0cmFuc2FjYW8gcXVlIHZhaQogICAtIGVudmlhciBwYWdhbWVudG9zIHBhcmEgQWxpY2UgZSBCb2IKICAgLSBlIGRlcG9pcyBhcGFnYXIgYSB0cmFuc2FjYW8gcXVlIHRlcmlhIGNyaWFkbyBvIGNhbmFsIGRlIHBhZ2FtZW50bwoKRmVjaGFuZG8gdW0gY2FuYWwgcXVhbmRvIEFsaWNlIGUgQm9iIG5hbyBjb25jb3JkYW0gY29tIG9zIHNhbGRvcyBmaW5haXMKMS4gSW1wbGVtZW50YSBjYW5hbCBkZSBwYWdhbWVudG8gZGEgbXVsdGktc2lnCjIuIGNoYW1hIGNoYWxsZW5nZUV4aXQoKXBhcmEgaW5pY2lhciBvIHByb2Nlc3NvIGRlIGZlY2hhbWVudG8gZG8gY2FuYWwKMy4gQWxpY2UgZSBCb2IgcG9kZW0gcmV0aXJhciBmdW5kb3MgdW1hIHZleiBxdWUgbyBjYW5hbCBlIGV4dGludG8KKi8KCmltcG9ydCAiZ2l0aHViLmNvbS9PcGVuWmVwcGVsaW4vb3BlbnplcHBlbGluLWNvbnRyYWN0cy9ibG9iL3JlbGVhc2UtdjQuNS9jb250cmFjdHMvdXRpbHMvY3J5cHRvZ3JhcGh5L0VDRFNBLnNvbCI7Cgpjb250cmFjdCBCaURpcmVjdGlvbmFsUGF5bWVudENoYW5uZWwgewogICAgdXNpbmcgRUNEU0EgZm9yIGJ5dGVzMzI7CgogICAgZXZlbnQgQ2hhbGxlbmdlRXhpdChhZGRyZXNzIGluZGV4ZWQgc2VuZGVyLCB1aW50IG5vbmNlKTsKICAgIGV2ZW50IFdpdGhkcmF3KGFkZHJlc3MgaW5kZXhlZCB0bywgdWludCBhbW91bnQpOwoKICAgIGFkZHJlc3MgcGF5YWJsZVsyXSBwdWJsaWMgdXNlcnM7CiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gYm9vbCkgcHVibGljIGlzVXNlcjsKCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gdWludCkgcHVibGljIGJhbGFuY2VzOwoKICAgIHVpbnQgcHVibGljIGNoYWxsZW5nZVBlcmlvZDsKICAgIHVpbnQgcHVibGljIGV4cGlyZXNBdDsKICAgIHVpbnQgcHVibGljIG5vbmNlOwoKICAgIG1vZGlmaWVyIGNoZWNrQmFsYW5jZXModWludFsyXSBtZW1vcnkgX2JhbGFuY2VzKSB7CiAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgYWRkcmVzcyh0aGlzKS5iYWxhbmNlID49IF9iYWxhbmNlc1swXSArIF9iYWxhbmNlc1sxXSwKICAgICAgICAgICAgImJhbGFuY2Ugb2YgY29udHJhY3QgbXVzdCBiZSA+PSB0byB0aGUgdG90YWwgYmFsYW5jZSBvZiB1c2VycyIKICAgICAgICApOwogICAgICAgIF87CiAgICB9CgogICAgLy8gTk9UQTogZGVwb3NpdGFyIGRlIHVtYSBjYXJ0ZWlyYSBtdWx0aS1zaWcKICAgIGNvbnN0cnVjdG9yKAogICAgICAgIGFkZHJlc3MgcGF5YWJsZVsyXSBtZW1vcnkgX3VzZXJzLAogICAgICAgIHVpbnRbMl0gbWVtb3J5IF9iYWxhbmNlcywKICAgICAgICB1aW50IF9leHBpcmVzQXQsCiAgICAgICAgdWludCBfY2hhbGxlbmdlUGVyaW9kCiAgICApIHBheWFibGUgY2hlY2tCYWxhbmNlcyhfYmFsYW5jZXMpIHsKICAgICAgICByZXF1aXJlKF9leHBpcmVzQXQgPiBibG9jay50aW1lc3RhbXAsICJFeHBpcmF0aW9uIG11c3QgYmUgPiBub3ciKTsKICAgICAgICByZXF1aXJlKF9jaGFsbGVuZ2VQZXJpb2QgPiAwLCAiQ2hhbGxlbmdlIHBlcmlvZCBtdXN0IGJlID4gMCIpOwoKICAgICAgICBmb3IgKHVpbnQgaSA9IDA7IGkgPCBfdXNlcnMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgYWRkcmVzcyBwYXlhYmxlIHVzZXIgPSBfdXNlcnNbaV07CgogICAgICAgICAgICByZXF1aXJlKCFpc1VzZXJbdXNlcl0sICJ1c2VyIG11c3QgYmUgdW5pcXVlIik7CiAgICAgICAgICAgIHVzZXJzW2ldID0gdXNlcjsKICAgICAgICAgICAgaXNVc2VyW3VzZXJdID0gdHJ1ZTsKCiAgICAgICAgICAgIGJhbGFuY2VzW3VzZXJdID0gX2JhbGFuY2VzW2ldOwogICAgICAgIH0KCiAgICAgICAgZXhwaXJlc0F0ID0gX2V4cGlyZXNBdDsKICAgICAgICBjaGFsbGVuZ2VQZXJpb2QgPSBfY2hhbGxlbmdlUGVyaW9kOwogICAgfQoKICAgIGZ1bmN0aW9uIHZlcmlmeSgKICAgICAgICBieXRlc1syXSBtZW1vcnkgX3NpZ25hdHVyZXMsCiAgICAgICAgYWRkcmVzcyBfY29udHJhY3QsCiAgICAgICAgYWRkcmVzc1syXSBtZW1vcnkgX3NpZ25lcnMsCiAgICAgICAgdWludFsyXSBtZW1vcnkgX2JhbGFuY2VzLAogICAgICAgIHVpbnQgX25vbmNlCiAgICApIHB1YmxpYyBwdXJlIHJldHVybnMgKGJvb2wpIHsKICAgICAgICBmb3IgKHVpbnQgaSA9IDA7IGkgPCBfc2lnbmF0dXJlcy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICAvKgogICAgICAgICAgICBOT1RBOiBhc3NpbmEgY29tIGVuZGVyZWNvIGRlc3NlIGNvbnRyYXRvIHBhcmEgcHJvdGVnZXIKICAgICAgICAgICAgICAgICAgY29udHJhIGF0YXF1ZSBkZSByZXBldGljYW8gZW0gb3V0cm9zIGNvbnRyYXRvcwogICAgICAgICAgICAqLwogICAgICAgICAgICBib29sIHZhbGlkID0gX3NpZ25lcnNbaV0gPT0KICAgICAgICAgICAgICAgIGtlY2NhazI1NihhYmkuZW5jb2RlUGFja2VkKF9jb250cmFjdCwgX2JhbGFuY2VzLCBfbm9uY2UpKQogICAgICAgICAgICAgICAgLnRvRXRoU2lnbmVkTWVzc2FnZUhhc2goKQogICAgICAgICAgICAgICAgLnJlY292ZXIoX3NpZ25hdHVyZXNbaV0pOwoKICAgICAgICAgICAgaWYgKCF2YWxpZCkgewogICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBtb2RpZmllciBjaGVja1NpZ25hdHVyZXMoCiAgICAgICAgYnl0ZXNbMl0gbWVtb3J5IF9zaWduYXR1cmVzLAogICAgICAgIHVpbnRbMl0gbWVtb3J5IF9iYWxhbmNlcywKICAgICAgICB1aW50IF9ub25jZQogICAgKSB7CiAgICAgICAgLy8gTm90ZTogY29weSBzdG9yYWdlIGFycmF5IHRvIG1lbW9yeQogICAgICAgIGFkZHJlc3NbMl0gbWVtb3J5IHNpZ25lcnM7CiAgICAgICAgZm9yICh1aW50IGkgPSAwOyBpIDwgdXNlcnMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgc2lnbmVyc1tpXSA9IHVzZXJzW2ldOwogICAgICAgIH0KCiAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgdmVyaWZ5KF9zaWduYXR1cmVzLCBhZGRyZXNzKHRoaXMpLCBzaWduZXJzLCBfYmFsYW5jZXMsIF9ub25jZSksCiAgICAgICAgICAgICJJbnZhbGlkIHNpZ25hdHVyZSIKICAgICAgICApOwoKICAgICAgICBfOwogICAgfQoKICAgIG1vZGlmaWVyIG9ubHlVc2VyKCkgewogICAgICAgIHJlcXVpcmUoaXNVc2VyW21zZy5zZW5kZXJdLCAiTm90IHVzZXIiKTsKICAgICAgICBfOwogICAgfQoKICAgIGZ1bmN0aW9uIGNoYWxsZW5nZUV4aXQoCiAgICAgICAgdWludFsyXSBtZW1vcnkgX2JhbGFuY2VzLAogICAgICAgIHVpbnQgX25vbmNlLAogICAgICAgIGJ5dGVzWzJdIG1lbW9yeSBfc2lnbmF0dXJlcwogICAgKQogICAgICAgIHB1YmxpYwogICAgICAgIG9ubHlVc2VyCiAgICAgICAgY2hlY2tTaWduYXR1cmVzKF9zaWduYXR1cmVzLCBfYmFsYW5jZXMsIF9ub25jZSkKICAgICAgICBjaGVja0JhbGFuY2VzKF9iYWxhbmNlcykKICAgIHsKICAgICAgICByZXF1aXJlKGJsb2NrLnRpbWVzdGFtcCA8IGV4cGlyZXNBdCwgIkV4cGlyZWQgY2hhbGxlbmdlIHBlcmlvZCIpOwogICAgICAgIHJlcXVpcmUoX25vbmNlID4gbm9uY2UsICJOb25jZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiB0aGUgY3VycmVudCBub25jZSIpOwoKICAgICAgICBmb3IgKHVpbnQgaSA9IDA7IGkgPCBfYmFsYW5jZXMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgYmFsYW5jZXNbdXNlcnNbaV1dID0gX2JhbGFuY2VzW2ldOwogICAgICAgIH0KCiAgICAgICAgbm9uY2UgPSBfbm9uY2U7CiAgICAgICAgZXhwaXJlc0F0ID0gYmxvY2sudGltZXN0YW1wICsgY2hhbGxlbmdlUGVyaW9kOwoKICAgICAgICBlbWl0IENoYWxsZW5nZUV4aXQobXNnLnNlbmRlciwgbm9uY2UpOwogICAgfQoKICAgIGZ1bmN0aW9uIHdpdGhkcmF3KCkgcHVibGljIG9ubHlVc2VyIHsKICAgICAgICByZXF1aXJlKGJsb2NrLnRpbWVzdGFtcCA+PSBleHBpcmVzQXQsICJDaGFsbGVuZ2UgcGVyaW9kIGhhcyBub3QgZXhwaXJlZCB5ZXQiKTsKCiAgICAgICAgdWludCBhbW91bnQgPSBiYWxhbmNlc1ttc2cuc2VuZGVyXTsKICAgICAgICBiYWxhbmNlc1ttc2cuc2VuZGVyXSA9IDA7CgogICAgICAgIChib29sIHNlbnQsICkgPSBtc2cuc2VuZGVyLmNhbGx7dmFsdWU6IGFtb3VudH0oIiIpOwogICAgICAgIHJlcXVpcmUoc2VudCwgIkZhaWxlZCB0byBzZW5kIEV0aGVyIik7CgogICAgICAgIGVtaXQgV2l0aGRyYXcobXNnLnNlbmRlciwgYW1vdW50KTsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/canal-de-pagamento-unidirecional.md b/src/exemplos/aplicacoes/canal-de-pagamento-unidirecional.md index 189e3a8..d9202aa 100644 --- a/src/exemplos/aplicacoes/canal-de-pagamento-unidirecional.md +++ b/src/exemplos/aplicacoes/canal-de-pagamento-unidirecional.md @@ -1,6 +1,6 @@ # Canal de Pagamento Unidirecional -Canais de pagamento permitem que participantes transfiram Ether off chain repetidamente +Os canais de pagamento permitem que os participantes transfiram repetidamente o Ether para fora da cadeia. Aqui está como esse contrato é usado: @@ -13,10 +13,10 @@ Esse é chamado um canal de pagamento unidirecional já que o pagamento só pode ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; -import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.3/contracts/cryptography/ECDSA.sol"; -import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.3/contracts/utils/ReentrancyGuard.sol"; +import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol"; +import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/security/ReentrancyGuard.sol"; contract UniDirectionalPaymentChannel is ReentrancyGuard { using ECDSA for bytes32; @@ -29,34 +29,34 @@ contract UniDirectionalPaymentChannel is ReentrancyGuard { constructor(address payable _receiver) payable { require(_receiver != address(0), "receiver = zero address"); - sender = msg.sender; + sender = payable(msg.sender); receiver = _receiver; expiresAt = block.timestamp + DURATION; } - function _getHash(uint _amount) private pure returns (bytes32) { - // NOTA: assine com endereço desse contrato para proteção contra + function _getHash(uint _amount) private view returns (bytes32) { + // NOTA: assine com endereço desse contrato para proteção contra // ataque de repetição em outros contratos return keccak256(abi.encodePacked(address(this), _amount)); } - function getHash(uint _amount) external pure returns (bytes32) { + function getHash(uint _amount) external view returns (bytes32) { return _getHash(_amount); } - function _getEthSignedHash(uint _amount) private pure returns (bytes32) { + function _getEthSignedHash(uint _amount) private view returns (bytes32) { return _getHash(_amount).toEthSignedMessageHash(); } - function getEthSignedHash(uint _amount) external pure returns (bytes32) { + function getEthSignedHash(uint _amount) external view returns (bytes32) { return _getEthSignedHash(_amount); } - function _verify(uint _amount bytes memory _sig) private view returns (bool) { + function _verify(uint _amount, bytes memory _sig) private view returns (bool) { return _getEthSignedHash(_amount).recover(_sig) == sender; } - function verify(uint _amount bytes memory _sig) external view returns (bool) { + function verify(uint _amount, bytes memory _sig) external view returns (bool) { return _verify(_amount, _sig); } @@ -76,3 +76,7 @@ contract UniDirectionalPaymentChannel is ReentrancyGuard { } } ``` + +## Teste no Remix + +- [UniDirectionalPaymentChannel.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmltcG9ydCAiZ2l0aHViLmNvbS9PcGVuWmVwcGVsaW4vb3BlbnplcHBlbGluLWNvbnRyYWN0cy9ibG9iL3JlbGVhc2UtdjQuNS9jb250cmFjdHMvdXRpbHMvY3J5cHRvZ3JhcGh5L0VDRFNBLnNvbCI7CmltcG9ydCAiZ2l0aHViLmNvbS9PcGVuWmVwcGVsaW4vb3BlbnplcHBlbGluLWNvbnRyYWN0cy9ibG9iL3JlbGVhc2UtdjQuNS9jb250cmFjdHMvc2VjdXJpdHkvUmVlbnRyYW5jeUd1YXJkLnNvbCI7Cgpjb250cmFjdCBVbmlEaXJlY3Rpb25hbFBheW1lbnRDaGFubmVsIGlzIFJlZW50cmFuY3lHdWFyZCB7CiAgICB1c2luZyBFQ0RTQSBmb3IgYnl0ZXMzMjsKCiAgICBhZGRyZXNzIHBheWFibGUgcHVibGljIHNlbmRlcjsKICAgIGFkZHJlc3MgcGF5YWJsZSBwdWJsaWMgcmVjZWl2ZXI7CgogICAgdWludCBwcml2YXRlIGNvbnN0YW50IERVUkFUSU9OID0gNyAqIDI0ICogNjAgKiA2MDsKICAgIHVpbnQgcHVibGljIGV4cGlyZXNBdDsKCiAgICBjb25zdHJ1Y3RvcihhZGRyZXNzIHBheWFibGUgX3JlY2VpdmVyKSBwYXlhYmxlIHsKICAgICAgICByZXF1aXJlKF9yZWNlaXZlciAhPSBhZGRyZXNzKDApLCAicmVjZWl2ZXIgPSB6ZXJvIGFkZHJlc3MiKTsKICAgICAgICBzZW5kZXIgPSBwYXlhYmxlKG1zZy5zZW5kZXIpOwogICAgICAgIHJlY2VpdmVyID0gX3JlY2VpdmVyOwogICAgICAgIGV4cGlyZXNBdCA9IGJsb2NrLnRpbWVzdGFtcCArIERVUkFUSU9OOwogICAgfQoKICAgIGZ1bmN0aW9uIF9nZXRIYXNoKHVpbnQgX2Ftb3VudCkgcHJpdmF0ZSB2aWV3IHJldHVybnMgKGJ5dGVzMzIpIHsKICAgICAgICAgLy8gTk9UQTogYXNzaW5lIGNvbSBlbmRlcmVjbyBkZXNzZSBjb250cmF0byBwYXJhIHByb3RlY2FvIGNvbnRyYQogICAgICAgIC8vIGF0YXF1ZSBkZSByZXBldGljYW8gZW0gb3V0cm9zIGNvbnRyYXRvcwogICAgICAgIHJldHVybiBrZWNjYWsyNTYoYWJpLmVuY29kZVBhY2tlZChhZGRyZXNzKHRoaXMpLCBfYW1vdW50KSk7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0SGFzaCh1aW50IF9hbW91bnQpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAoYnl0ZXMzMikgewogICAgICAgIHJldHVybiBfZ2V0SGFzaChfYW1vdW50KTsKICAgIH0KCiAgICBmdW5jdGlvbiBfZ2V0RXRoU2lnbmVkSGFzaCh1aW50IF9hbW91bnQpIHByaXZhdGUgdmlldyByZXR1cm5zIChieXRlczMyKSB7CiAgICAgICAgcmV0dXJuIF9nZXRIYXNoKF9hbW91bnQpLnRvRXRoU2lnbmVkTWVzc2FnZUhhc2goKTsKICAgIH0KCiAgICBmdW5jdGlvbiBnZXRFdGhTaWduZWRIYXNoKHVpbnQgX2Ftb3VudCkgZXh0ZXJuYWwgdmlldyByZXR1cm5zIChieXRlczMyKSB7CiAgICAgICAgcmV0dXJuIF9nZXRFdGhTaWduZWRIYXNoKF9hbW91bnQpOwogICAgfQoKICAgIGZ1bmN0aW9uIF92ZXJpZnkodWludCBfYW1vdW50LCBieXRlcyBtZW1vcnkgX3NpZykgcHJpdmF0ZSB2aWV3IHJldHVybnMgKGJvb2wpIHsKICAgICAgICByZXR1cm4gX2dldEV0aFNpZ25lZEhhc2goX2Ftb3VudCkucmVjb3Zlcihfc2lnKSA9PSBzZW5kZXI7CiAgICB9CgogICAgZnVuY3Rpb24gdmVyaWZ5KHVpbnQgX2Ftb3VudCwgYnl0ZXMgbWVtb3J5IF9zaWcpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAoYm9vbCkgewogICAgICAgIHJldHVybiBfdmVyaWZ5KF9hbW91bnQsIF9zaWcpOwogICAgfQoKICAgIGZ1bmN0aW9uIGNsb3NlKHVpbnQgX2Ftb3VudCwgYnl0ZXMgbWVtb3J5IF9zaWcpIGV4dGVybmFsIG5vblJlZW50cmFudCB7CiAgICAgICAgcmVxdWlyZShtc2cuc2VuZGVyID09IHJlY2VpdmVyLCAiIXJlY2VpdmVyIik7CiAgICAgICAgcmVxdWlyZShfdmVyaWZ5KF9hbW91bnQsIF9zaWcpLCAiaW52YWxpZCBzaWciKTsKCiAgICAgICAgKGJvb2wgc2VudCwgKSA9IHJlY2VpdmVyLmNhbGx7dmFsdWU6IF9hbW91bnR9KCIiKTsKICAgICAgICByZXF1aXJlKHNlbnQsICJGYWlsZWQgdG8gc2VuZCBFdGhlciIpOwogICAgICAgIHNlbGZkZXN0cnVjdChzZW5kZXIpOwogICAgfQoKICAgIGZ1bmN0aW9uIGNhbmNlbCgpIGV4dGVybmFsIHsKICAgICAgICByZXF1aXJlKG1zZy5zZW5kZXIgPT0gc2VuZGVyLCAiIXNlbmRlciIpOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wID49IGV4cGlyZXNBdCwgIiFleHBpcmVkIik7CiAgICAgICAgc2VsZmRlc3RydWN0KHNlbmRlcik7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) \ No newline at end of file diff --git a/src/exemplos/aplicacoes/carteira-ether.md b/src/exemplos/aplicacoes/carteira-ether.md new file mode 100644 index 0000000..6561ddb --- /dev/null +++ b/src/exemplos/aplicacoes/carteira-ether.md @@ -0,0 +1,34 @@ +# Carteira Ether + +Um exemplo de uma carteira básica. + +- Qualquer pessoa pode enviar ETH. +- Somente o proprietário pode retirar. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract EtherWallet { + address payable public owner; + + constructor() { + owner = payable(msg.sender); + } + + receive() external payable {} + + function withdraw(uint _amount) external { + require(msg.sender == owner, "Quem está chamando não é o dono"); + payable(msg.sender).transfer(_amount); + } + + function getBalance() external view returns (uint) { + return address(this).balance; + } +} +``` + +## Teste no Remix + +- [EtherWallet.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IEV0aGVyV2FsbGV0IHsKICAgIGFkZHJlc3MgcGF5YWJsZSBwdWJsaWMgb3duZXI7CgogICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgb3duZXIgPSBwYXlhYmxlKG1zZy5zZW5kZXIpOwogICAgfQoKICAgIHJlY2VpdmUoKSBleHRlcm5hbCBwYXlhYmxlIHt9CgogICAgZnVuY3Rpb24gd2l0aGRyYXcodWludCBfYW1vdW50KSBleHRlcm5hbCB7CiAgICAgICAgcmVxdWlyZShtc2cuc2VuZGVyID09IG93bmVyLCAiUXVlbSBlc3RhIGNoYW1hbmRvIG5hbyBlIG8gZG9ubyIpOwogICAgICAgIHBheWFibGUobXNnLnNlbmRlcikudHJhbnNmZXIoX2Ftb3VudCk7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0QmFsYW5jZSgpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAodWludCkgewogICAgICAgIHJldHVybiBhZGRyZXNzKHRoaXMpLmJhbGFuY2U7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) \ No newline at end of file diff --git a/src/exemplos/aplicacoes/carteira-multi-assinaturas.md b/src/exemplos/aplicacoes/carteira-multi-assinaturas.md index 6e51d76..c9a52fa 100644 --- a/src/exemplos/aplicacoes/carteira-multi-assinaturas.md +++ b/src/exemplos/aplicacoes/carteira-multi-assinaturas.md @@ -4,13 +4,13 @@ Vamos criar uma carteira multi-sig. Aqui estão as especificações. Os proprietários da carteira podem -* submeter uma transação -* aprovar e revogar aprovação de transações pendentes -* qualquer um pode executar uma transação depois que os proprietários tenham aprovado. +- submeter uma transação +- aprovar e revogar aprovação de transações pendentes +- qualquer um pode executar uma transação depois que os proprietários tenham aprovado. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; contract MultiSigWallet { event Deposit(address indexed sender, uint amount, uint balance); @@ -43,38 +43,38 @@ contract MultiSigWallet { Transaction[] public transactions; modifier onlyOwner() { - require(isOwner[msg.sender], "not owner"); + require(isOwner[msg.sender], "Não é o dono"); _; } modifier txExists(uint _txIndex) { - require(_txIndex < transactions.length, "tx does not exist"); + require(_txIndex < transactions.length, "tx não existe"); _; } modifier notExecuted(uint _txIndex) { - require(!transactions[_txIndex].executed, "tx already executed"); + require(!transactions[_txIndex].executed, "tx já executado"); _; } modifier notConfirmed(uint _txIndex) { - require(!isConfirmed[_txIndex][msg.sender], "tx already confirmed"); + require(!isConfirmed[_txIndex][msg.sender], "tx já confirmado"); _; } constructor(address[] memory _owners, uint _numConfirmationsRequired) { - require(_owners.length > 0, "owners required"); + require(_owners.length > 0, "precisa do dono"); require( _numConfirmationsRequired > 0 && _numConfirmationsRequired <= _owners.length, - "invalid number of required confirmations" + "número inválido de confirmações necessárias" ); for (uint i = 0; i < _owners.length; i++) { address owner = _owners[i]; - require(owner != address(0), "invalid owner"); - require(!isOwner[owner], "owner not unique"); + require(owner != address(0), "dono inválido"); + require(!isOwner[owner], "dono n]ap é único"); isOwner[owner] = true; owners.push(owner); @@ -131,7 +131,7 @@ contract MultiSigWallet { require( transaction.numConfirmations >= numConfirmationsRequired, - "cannot execute tx" + "não pode executar tx" ); transaction.executed = true; @@ -139,7 +139,7 @@ contract MultiSigWallet { (bool success, ) = transaction.to.call{value: transaction.value}( transaction.data ); - require(success, "tx failed"); + require(success, "tx falhou"); emit ExecuteTransaction(msg.sender, _txIndex); } @@ -152,7 +152,7 @@ contract MultiSigWallet { { Transaction storage transaction = transactions[_txIndex]; - require(isConfirmed[_txIndex][msg.sender], "tx not confirmed"); + require(isConfirmed[_txIndex][msg.sender], "tx não confirmado"); transaction.numConfirmations -= 1; isConfirmed[_txIndex][msg.sender] = false; @@ -192,11 +192,11 @@ contract MultiSigWallet { } ``` -Eis aqui um contrato para testar envio de transações de uma carteira multi-sig +Aqui está um contrato para testar o envio de transações da carteira multi-sig ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; contract TestContract { uint public i; @@ -210,3 +210,8 @@ contract TestContract { } } ``` + +## Teste no Remix + +- [MultiSigWallet.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IE11bHRpU2lnV2FsbGV0IHsKICAgIGV2ZW50IERlcG9zaXQoYWRkcmVzcyBpbmRleGVkIHNlbmRlciwgdWludCBhbW91bnQsIHVpbnQgYmFsYW5jZSk7CiAgICBldmVudCBTdWJtaXRUcmFuc2FjdGlvbigKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgb3duZXIsCiAgICAgICAgdWludCBpbmRleGVkIHR4SW5kZXgsCiAgICAgICAgYWRkcmVzcyBpbmRleGVkIHRvLAogICAgICAgIHVpbnQgdmFsdWUsCiAgICAgICAgYnl0ZXMgZGF0YQogICAgKTsKICAgIGV2ZW50IENvbmZpcm1UcmFuc2FjdGlvbihhZGRyZXNzIGluZGV4ZWQgb3duZXIsIHVpbnQgaW5kZXhlZCB0eEluZGV4KTsKICAgIGV2ZW50IFJldm9rZUNvbmZpcm1hdGlvbihhZGRyZXNzIGluZGV4ZWQgb3duZXIsIHVpbnQgaW5kZXhlZCB0eEluZGV4KTsKICAgIGV2ZW50IEV4ZWN1dGVUcmFuc2FjdGlvbihhZGRyZXNzIGluZGV4ZWQgb3duZXIsIHVpbnQgaW5kZXhlZCB0eEluZGV4KTsKCiAgICBhZGRyZXNzW10gcHVibGljIG93bmVyczsKICAgIG1hcHBpbmcoYWRkcmVzcyA9PiBib29sKSBwdWJsaWMgaXNPd25lcjsKICAgIHVpbnQgcHVibGljIG51bUNvbmZpcm1hdGlvbnNSZXF1aXJlZDsKCiAgICBzdHJ1Y3QgVHJhbnNhY3Rpb24gewogICAgICAgIGFkZHJlc3MgdG87CiAgICAgICAgdWludCB2YWx1ZTsKICAgICAgICBieXRlcyBkYXRhOwogICAgICAgIGJvb2wgZXhlY3V0ZWQ7CiAgICAgICAgdWludCBudW1Db25maXJtYXRpb25zOwogICAgfQoKICAgIC8vIG1hcHBpbmcgZGUgdHggaW5kZXggPT4gb3duZXIgPT4gYm9vbAogICAgbWFwcGluZyh1aW50ID0+IG1hcHBpbmcoYWRkcmVzcyA9PiBib29sKSkgcHVibGljIGlzQ29uZmlybWVkOwoKICAgIFRyYW5zYWN0aW9uW10gcHVibGljIHRyYW5zYWN0aW9uczsKCiAgICBtb2RpZmllciBvbmx5T3duZXIoKSB7CiAgICAgICAgcmVxdWlyZShpc093bmVyW21zZy5zZW5kZXJdLCAiTmFvIGUgIG8gZG9ubyIpOwogICAgICAgIF87CiAgICB9CgogICAgbW9kaWZpZXIgdHhFeGlzdHModWludCBfdHhJbmRleCkgewogICAgICAgIHJlcXVpcmUoX3R4SW5kZXggPCB0cmFuc2FjdGlvbnMubGVuZ3RoLCAidHggbmFvIGV4aXN0ZSIpOwogICAgICAgIF87CiAgICB9CgogICAgbW9kaWZpZXIgbm90RXhlY3V0ZWQodWludCBfdHhJbmRleCkgewogICAgICAgIHJlcXVpcmUoIXRyYW5zYWN0aW9uc1tfdHhJbmRleF0uZXhlY3V0ZWQsICJ0eCBqYSBleGVjdXRhZG8iKTsKICAgICAgICBfOwogICAgfQoKICAgIG1vZGlmaWVyIG5vdENvbmZpcm1lZCh1aW50IF90eEluZGV4KSB7CiAgICAgICAgcmVxdWlyZSghaXNDb25maXJtZWRbX3R4SW5kZXhdW21zZy5zZW5kZXJdLCAidHggamEgY29uZmlybWFkbyIpOwogICAgICAgIF87CiAgICB9CgogICAgY29uc3RydWN0b3IoYWRkcmVzc1tdIG1lbW9yeSBfb3duZXJzLCB1aW50IF9udW1Db25maXJtYXRpb25zUmVxdWlyZWQpIHsKICAgICAgICByZXF1aXJlKF9vd25lcnMubGVuZ3RoID4gMCwgInByZWNpc2EgZG8gZG9ubyIpOwogICAgICAgIHJlcXVpcmUoCiAgICAgICAgICAgIF9udW1Db25maXJtYXRpb25zUmVxdWlyZWQgPiAwICYmCiAgICAgICAgICAgICAgICBfbnVtQ29uZmlybWF0aW9uc1JlcXVpcmVkIDw9IF9vd25lcnMubGVuZ3RoLAogICAgICAgICAgICAibnVtZXJvIGludmFsaWRvIGRlIGNvbmZpcm1hY29lcyBuZWNlc3NhcmlhcyIKICAgICAgICApOwoKICAgICAgICBmb3IgKHVpbnQgaSA9IDA7IGkgPCBfb3duZXJzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIGFkZHJlc3Mgb3duZXIgPSBfb3duZXJzW2ldOwoKICAgICAgICAgICAgcmVxdWlyZShvd25lciAhPSBhZGRyZXNzKDApLCAiZG9ubyBpbnZhbGlkbyIpOwogICAgICAgICAgICByZXF1aXJlKCFpc093bmVyW293bmVyXSwgImRvbm8gbl1hcCBlIHVuaWNvIik7CgogICAgICAgICAgICBpc093bmVyW293bmVyXSA9IHRydWU7CiAgICAgICAgICAgIG93bmVycy5wdXNoKG93bmVyKTsKICAgICAgICB9CgogICAgICAgIG51bUNvbmZpcm1hdGlvbnNSZXF1aXJlZCA9IF9udW1Db25maXJtYXRpb25zUmVxdWlyZWQ7CiAgICB9CgogICAgcmVjZWl2ZSgpIGV4dGVybmFsIHBheWFibGUgewogICAgICAgIGVtaXQgRGVwb3NpdChtc2cuc2VuZGVyLCBtc2cudmFsdWUsIGFkZHJlc3ModGhpcykuYmFsYW5jZSk7CiAgICB9CgogICAgZnVuY3Rpb24gc3VibWl0VHJhbnNhY3Rpb24oCiAgICAgICAgYWRkcmVzcyBfdG8sCiAgICAgICAgdWludCBfdmFsdWUsCiAgICAgICAgYnl0ZXMgbWVtb3J5IF9kYXRhCiAgICApIHB1YmxpYyBvbmx5T3duZXIgewogICAgICAgIHVpbnQgdHhJbmRleCA9IHRyYW5zYWN0aW9ucy5sZW5ndGg7CgogICAgICAgIHRyYW5zYWN0aW9ucy5wdXNoKAogICAgICAgICAgICBUcmFuc2FjdGlvbih7CiAgICAgICAgICAgICAgICB0bzogX3RvLAogICAgICAgICAgICAgICAgdmFsdWU6IF92YWx1ZSwKICAgICAgICAgICAgICAgIGRhdGE6IF9kYXRhLAogICAgICAgICAgICAgICAgZXhlY3V0ZWQ6IGZhbHNlLAogICAgICAgICAgICAgICAgbnVtQ29uZmlybWF0aW9uczogMAogICAgICAgICAgICB9KQogICAgICAgICk7CgogICAgICAgIGVtaXQgU3VibWl0VHJhbnNhY3Rpb24obXNnLnNlbmRlciwgdHhJbmRleCwgX3RvLCBfdmFsdWUsIF9kYXRhKTsKICAgIH0KCiAgICBmdW5jdGlvbiBjb25maXJtVHJhbnNhY3Rpb24odWludCBfdHhJbmRleCkKICAgICAgICBwdWJsaWMKICAgICAgICBvbmx5T3duZXIKICAgICAgICB0eEV4aXN0cyhfdHhJbmRleCkKICAgICAgICBub3RFeGVjdXRlZChfdHhJbmRleCkKICAgICAgICBub3RDb25maXJtZWQoX3R4SW5kZXgpCiAgICB7CiAgICAgICAgVHJhbnNhY3Rpb24gc3RvcmFnZSB0cmFuc2FjdGlvbiA9IHRyYW5zYWN0aW9uc1tfdHhJbmRleF07CiAgICAgICAgdHJhbnNhY3Rpb24ubnVtQ29uZmlybWF0aW9ucyArPSAxOwogICAgICAgIGlzQ29uZmlybWVkW190eEluZGV4XVttc2cuc2VuZGVyXSA9IHRydWU7CgogICAgICAgIGVtaXQgQ29uZmlybVRyYW5zYWN0aW9uKG1zZy5zZW5kZXIsIF90eEluZGV4KTsKICAgIH0KCiAgICBmdW5jdGlvbiBleGVjdXRlVHJhbnNhY3Rpb24odWludCBfdHhJbmRleCkKICAgICAgICBwdWJsaWMKICAgICAgICBvbmx5T3duZXIKICAgICAgICB0eEV4aXN0cyhfdHhJbmRleCkKICAgICAgICBub3RFeGVjdXRlZChfdHhJbmRleCkKICAgIHsKICAgICAgICBUcmFuc2FjdGlvbiBzdG9yYWdlIHRyYW5zYWN0aW9uID0gdHJhbnNhY3Rpb25zW190eEluZGV4XTsKCiAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgdHJhbnNhY3Rpb24ubnVtQ29uZmlybWF0aW9ucyA+PSBudW1Db25maXJtYXRpb25zUmVxdWlyZWQsCiAgICAgICAgICAgICJuYW8gcG9kZSBleGVjdXRhciB0eCIKICAgICAgICApOwoKICAgICAgICB0cmFuc2FjdGlvbi5leGVjdXRlZCA9IHRydWU7CgogICAgICAgIChib29sIHN1Y2Nlc3MsICkgPSB0cmFuc2FjdGlvbi50by5jYWxse3ZhbHVlOiB0cmFuc2FjdGlvbi52YWx1ZX0oCiAgICAgICAgICAgIHRyYW5zYWN0aW9uLmRhdGEKICAgICAgICApOwogICAgICAgIHJlcXVpcmUoc3VjY2VzcywgInR4IGZhbGhvdSIpOwoKICAgICAgICBlbWl0IEV4ZWN1dGVUcmFuc2FjdGlvbihtc2cuc2VuZGVyLCBfdHhJbmRleCk7CiAgICB9CgogICAgZnVuY3Rpb24gcmV2b2tlQ29uZmlybWF0aW9uKHVpbnQgX3R4SW5kZXgpCiAgICAgICAgcHVibGljCiAgICAgICAgb25seU93bmVyCiAgICAgICAgdHhFeGlzdHMoX3R4SW5kZXgpCiAgICAgICAgbm90RXhlY3V0ZWQoX3R4SW5kZXgpCiAgICB7CiAgICAgICAgVHJhbnNhY3Rpb24gc3RvcmFnZSB0cmFuc2FjdGlvbiA9IHRyYW5zYWN0aW9uc1tfdHhJbmRleF07CgogICAgICAgIHJlcXVpcmUoaXNDb25maXJtZWRbX3R4SW5kZXhdW21zZy5zZW5kZXJdLCAidHggbmFvIGNvbmZpcm1hZG8iKTsKCiAgICAgICAgdHJhbnNhY3Rpb24ubnVtQ29uZmlybWF0aW9ucyAtPSAxOwogICAgICAgIGlzQ29uZmlybWVkW190eEluZGV4XVttc2cuc2VuZGVyXSA9IGZhbHNlOwoKICAgICAgICBlbWl0IFJldm9rZUNvbmZpcm1hdGlvbihtc2cuc2VuZGVyLCBfdHhJbmRleCk7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0T3duZXJzKCkgcHVibGljIHZpZXcgcmV0dXJucyAoYWRkcmVzc1tdIG1lbW9yeSkgewogICAgICAgIHJldHVybiBvd25lcnM7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0VHJhbnNhY3Rpb25Db3VudCgpIHB1YmxpYyB2aWV3IHJldHVybnMgKHVpbnQpIHsKICAgICAgICByZXR1cm4gdHJhbnNhY3Rpb25zLmxlbmd0aDsKICAgIH0KCiAgICBmdW5jdGlvbiBnZXRUcmFuc2FjdGlvbih1aW50IF90eEluZGV4KQogICAgICAgIHB1YmxpYwogICAgICAgIHZpZXcKICAgICAgICByZXR1cm5zICgKICAgICAgICAgICAgYWRkcmVzcyB0bywKICAgICAgICAgICAgdWludCB2YWx1ZSwKICAgICAgICAgICAgYnl0ZXMgbWVtb3J5IGRhdGEsCiAgICAgICAgICAgIGJvb2wgZXhlY3V0ZWQsCiAgICAgICAgICAgIHVpbnQgbnVtQ29uZmlybWF0aW9ucwogICAgICAgICkKICAgIHsKICAgICAgICBUcmFuc2FjdGlvbiBzdG9yYWdlIHRyYW5zYWN0aW9uID0gdHJhbnNhY3Rpb25zW190eEluZGV4XTsKCiAgICAgICAgcmV0dXJuICgKICAgICAgICAgICAgdHJhbnNhY3Rpb24udG8sCiAgICAgICAgICAgIHRyYW5zYWN0aW9uLnZhbHVlLAogICAgICAgICAgICB0cmFuc2FjdGlvbi5kYXRhLAogICAgICAgICAgICB0cmFuc2FjdGlvbi5leGVjdXRlZCwKICAgICAgICAgICAgdHJhbnNhY3Rpb24ubnVtQ29uZmlybWF0aW9ucwogICAgICAgICk7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) +- [TestContract.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IFRlc3RDb250cmFjdCB7CiAgICB1aW50IHB1YmxpYyBpOwoKICAgIGZ1bmN0aW9uIGNhbGxNZSh1aW50IGopIHB1YmxpYyB7CiAgICAgICAgaSArPSBqOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldERhdGEoKSBwdWJsaWMgcHVyZSByZXR1cm5zIChieXRlcyBtZW1vcnkpIHsKICAgICAgICByZXR1cm4gYWJpLmVuY29kZVdpdGhTaWduYXR1cmUoImNhbGxNZSh1aW50MjU2KSIsIDEyMyk7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/contrato-de-bytecode-simples.md b/src/exemplos/aplicacoes/contrato-de-bytecode-simples.md new file mode 100644 index 0000000..b0a7db5 --- /dev/null +++ b/src/exemplos/aplicacoes/contrato-de-bytecode-simples.md @@ -0,0 +1,66 @@ +# Contrato de Bytecode simples + +Exemplo simples de contrato escrito em bytecode + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract Factory { + event Log(address addr); + + // Deploys a contract that always returns 42 + function deploy() external { + bytes memory bytecode = hex"69602a60005260206000f3600052600a6016f3"; + address addr; + assembly { + // create(value, offset, size) + addr := create(0, add(bytecode, 0x20), 0x13) + } + require(addr != address(0)); + + emit Log(addr); + } +} + +interface IContract { + function getMeaningOfLife() external view returns (uint); +} + +// https://www.evm.codes/playground +/* +Run time code - return 42 +602a60005260206000f3 + +// Store 42 to memory +mstore(p, v) - store v at memory p to p + 32 + +PUSH1 0x2a +PUSH1 0 +MSTORE + +// Return 32 bytes from memory +return(p, s) - end execution and return data from memory p to p + s + +PUSH1 0x20 +PUSH1 0 +RETURN + +Creation code - return runtime code +69602a60005260206000f3600052600a6016f3 + +// Store run time code to memory +PUSH10 0X602a60005260206000f3 +PUSH1 0 +MSTORE + +// Return 10 bytes from memory starting at offset 22 +PUSH1 0x0a +PUSH1 0x16 +RETURN +*/ +``` + +## Teste no Remix + +- [Factory.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IEZhY3RvcnkgewogICAgZXZlbnQgTG9nKGFkZHJlc3MgYWRkcik7CgogICAgLy8gRGVwbG95cyBhIGNvbnRyYWN0IHRoYXQgYWx3YXlzIHJldHVybnMgNDIKICAgIGZ1bmN0aW9uIGRlcGxveSgpIGV4dGVybmFsIHsKICAgICAgICBieXRlcyBtZW1vcnkgYnl0ZWNvZGUgPSBoZXhcIjY5NjAyYTYwMDA1MjYwMjA2MDAwZjM2MDAwNTI2MDBhNjAxNmYzXCI7CiAgICAgICAgYWRkcmVzcyBhZGRyOwogICAgICAgIGFzc2VtYmx5IHsKICAgICAgICAgICAgLy8gY3JlYXRlKHZhbHVlLCBvZmZzZXQsIHNpemUpCiAgICAgICAgICAgIGFkZHIgOj0gY3JlYXRlKDAsIGFkZChieXRlY29kZSwgMHgyMCksIDB4MTMpCiAgICAgICAgfQogICAgICAgIHJlcXVpcmUoYWRkciAhPSBhZGRyZXNzKDApKTsKCiAgICAgICAgZW1pdCBMb2coYWRkcik7CiAgICB9Cn0KCmludGVyZmFjZSBJQ29udHJhY3QgewogICAgZnVuY3Rpb24gZ2V0TWVhbmluZ09mTGlmZSgpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAodWludCk7Cn0KCi8vIGh0dHBzOi8vd3d3LmV2bS5jb2Rlcy9wbGF5Z3JvdW5kCi8qClJ1biB0aW1lIGNvZGUgLSByZXR1cm4gNDIKNjAyYTYwMDA1MjYwMjA2MDAwZjMKCi8vIFN0b3JlIDQyIHRvIG1lbW9yeQptc3RvcmUocCwgdikgLSBzdG9yZSB2IGF0IG1lbW9yeSBwIHRvIHAgKyAzMgoKUFVTSDEgMHgyYQpQVVNIMSAwCk1TVE9SRQoKLy8gUmV0dXJuIDMyIGJ5dGVzIGZyb20gbWVtb3J5CnJldHVybihwLCBzKSAtIGVuZCBleGVjdXRpb24gYW5kIHJldHVybiBkYXRhIGZyb20gbWVtb3J5IHAgdG8gcCArIHMKClBVU0gxIDB4MjAKUFVTSDEgMApSRVRVUk4KCkNyZWF0aW9uIGNvZGUgLSByZXR1cm4gcnVudGltZSBjb2RlCjY5NjAyYTYwMDA1MjYwMjA2MDAwZjM2MDAwNTI2MDBhNjAxNmYzCgovLyBTdG9yZSBydW4gdGltZSBjb2RlIHRvIG1lbW9yeQpQVVNIMTAgMFg2MDJhNjAwMDUyNjAyMDYwMDBmMwpQVVNIMSAwCk1TVE9SRQoKLy8gUmV0dXJuIDEwIGJ5dGVzIGZyb20gbWVtb3J5IHN0YXJ0aW5nIGF0IG9mZnNldCAyMgpQVVNIMSAweDBhClBVU0gxIDB4MTYKUkVUVVJOCiov=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/contrato-proxy-minimo.md b/src/exemplos/aplicacoes/contrato-proxy-minimo.md index ab80658..04aa504 100644 --- a/src/exemplos/aplicacoes/contrato-proxy-minimo.md +++ b/src/exemplos/aplicacoes/contrato-proxy-minimo.md @@ -1,10 +1,10 @@ # Contrato Proxy Mínimo -Se você tem um contrato que será implantado múltiplas vezes, use o contrato proxy mínimo para implantá-los com baixo custo. +Se você tem um contrato que será implantado várias vezes, use o contrato de proxy mínimo para implantá-los de forma econômica. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; // código original // https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol @@ -30,7 +30,7 @@ contract MinimalProxy { Lê os 32 bytes da memória começando no ponteiro armazenado em 0x40 No solidity, o slot 0x40 na memória é especial: ele contém o "ponteiro de memória livre" - que aponta para o fim da memória corrente alocada. + que aponta para o fim da memória corrente alocada. */ let clone := mload(0x40) // armazena 32 bytes de memória começando pelo "clone" @@ -71,7 +71,11 @@ contract MinimalProxy { // código começa pelo ponteiro armazenado no "clone" // tamanho do código 0x37 (55 bytes) result := create(0, clone, 0x37) - } + } } } ``` + +## Teste no Remix + +- [MinimalProxy.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCi8vIGNvZGlnbyBvcmlnaW5hbAovLyBodHRwczovL2dpdGh1Yi5jb20vb3B0aW9uYWxpdHkvY2xvbmUtZmFjdG9yeS9ibG9iL21hc3Rlci9jb250cmFjdHMvQ2xvbmVGYWN0b3J5LnNvbAoKY29udHJhY3QgTWluaW1hbFByb3h5IHsKICAgIGZ1bmN0aW9uIGNsb25lKGFkZHJlc3MgdGFyZ2V0KSBleHRlcm5hbCByZXR1cm5zIChhZGRyZXNzIHJlc3VsdCkgewogICAgICAgIC8vIGNvbnZlcnRlIG8gZW5kZXJlY28gcGFyYSAyMCBieXRlcwogICAgICAgIGJ5dGVzMjAgdGFyZ2V0Qnl0ZXMgPSBieXRlczIwKHRhcmdldCk7CgogICAgICAgIC8vIGNvZGlnbyByZWFsIC8vCiAgICAgICAgLy8gM2Q2MDJkODA2MDBhM2QzOTgxZjMzNjNkM2QzNzNkM2QzZDM2M2Q3M2JlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmU1YWY0M2Q4MjgwM2U5MDNkOTE2MDJiNTdmZDViZjMKCiAgICAgICAgLy8gY3JpYWNhbyBkbyBjb2RpZ28gLy8KICAgICAgICAvLyBjb3BpYSBvIGNvZGlnbyBkbyB0ZW1wbyBkZSBleGVjdWNhbyBuYSBtZW1vcmlhIGUgcmV0b3JuYSBlc3NlIGNvZGlnbwogICAgICAgIC8vIDNkNjAyZDgwNjAwYTNkMzk4MWYzCgogICAgICAgIC8vIGNvZGlnbyBlbSB0ZW1wbyBkZSBleGVjdWNhbyAvLwogICAgICAgIC8vIGNvZGlnbyBwYXJhIGRlbGVnYXRlY2FsbCBwYXJhIGVuZGVyZWNvCiAgICAgICAgLy8gMzYzZDNkMzczZDNkM2QzNjNkNzMgYWRkcmVzcyA1YWY0M2Q4MjgwM2U5MDNkOTE2MDJiNTdmZDViZjMKCiAgICAgICAgYXNzZW1ibHkgewogICAgICAgICAgICAvKgogICAgICAgICAgICBMZSBvcyAzMiBieXRlcyBkYSBtZW1vcmlhIGNvbWVjYW5kbyBubyBwb250ZWlybyBhcm1hemVuYWRvIGVtIDB4NDAKCiAgICAgICAgICAgIE5vIHNvbGlkaXR5LCBvIHNsb3QgMHg0MCBuYSBtZW1vcmlhIGUgZXNwZWNpYWw6IGVsZSBjb250ZW0gbyAicG9udGVpcm8gZGUgbWVtb3JpYSBsaXZyZSIKICAgICAgICAgICAgcXVlIGFwb250YSBwYXJhIG8gZmltIGRhIG1lbW9yaWEgY29ycmVudGUgYWxvY2FkYS4KICAgICAgICAgICAgKi8KICAgICAgICAgICAgbGV0IGNsb25lIDo9IG1sb2FkKDB4NDApCiAgICAgICAgICAgIC8vIGFybWF6ZW5hIDMyIGJ5dGVzIGRlIG1lbW9yaWEgY29tZWNhbmRvIHBlbG8gImNsb25lIgogICAgICAgICAgICBtc3RvcmUoCiAgICAgICAgICAgICAgICBjbG9uZSwKICAgICAgICAgICAgICAgIDB4M2Q2MDJkODA2MDBhM2QzOTgxZjMzNjNkM2QzNzNkM2QzZDM2M2Q3MzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAogICAgICAgICAgICApCgogICAgICAgICAgICAvKgogICAgICAgICAgICAgIHwgICAgICAgICAgICAgIDIwIGJ5dGVzICAgICAgICAgICAgICAgIHwKICAgICAgICAgICAgMHgzZDYwMmQ4MDYwMGEzZDM5ODFmMzM2M2QzZDM3M2QzZDNkMzYzZDczMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRlcgogICAgICAgICAgICAqLwogICAgICAgICAgICAvLyBhcm1hemVuYSAzMiBieXRlcyBkZSBtZW1vcmlhIGluaWNpYW5kbyBwZWxvICJjbG9uZSIgKyAyMCBieXRlcwogICAgICAgICAgICAvLyAweDE0ID0gMjAKICAgICAgICAgICAgbXN0b3JlKGFkZChjbG9uZSwgMHgxNCksIHRhcmdldEJ5dGVzKQoKICAgICAgICAgICAgLyoKICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgMjAgYnl0ZXMgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAyMCBieXRlcyAgICAgICAgICAgICAgfAogICAgICAgICAgICAweDNkNjAyZDgwNjAwYTNkMzk4MWYzMzYzZDNkMzczZDNkM2QzNjNkNzNiZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBeCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb2ludGVyCiAgICAgICAgICAgICovCiAgICAgICAgICAgIC8vIGFybWF6ZW5hIDMyIGJ5dGVzIGRlIG1lbW9yaWEgaW5pY2lhbmRvIHBlbG8gImNsb25lIiArIDQwIGJ5dGVzCiAgICAgICAgICAgIC8vIDB4MjggPSA0MAogICAgICAgICAgICBtc3RvcmUoCiAgICAgICAgICAgICAgICBhZGQoY2xvbmUsIDB4MjgpLAogICAgICAgICAgICAgICAgMHg1YWY0M2Q4MjgwM2U5MDNkOTE2MDJiNTdmZDViZjMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCiAgICAgICAgICAgICkKCiAgICAgICAgICAgIC8qCiAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgIDIwIGJ5dGVzICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgMjAgYnl0ZXMgICAgICAgICAgICAgIHwgICAgICAgICAgIDE1IGJ5dGVzICAgICAgICAgIHwKICAgICAgICAgICAgMHgzZDYwMmQ4MDYwMGEzZDM5ODFmMzM2M2QzZDM3M2QzZDNkMzYzZDczYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZWJlYmViZTVhZjQzZDgyODAzZTkwM2Q5MTYwMmI1N2ZkNWJmMwogICAgICAgICAgICAqLwogICAgICAgICAgICAvLyBjcmlhIG5vdm8gY29udHJhdG8KICAgICAgICAgICAgLy8gZW52aWEgMCBFdGhlcgogICAgICAgICAgICAvLyBjb2RpZ28gY29tZWNhIHBlbG8gcG9udGVpcm8gYXJtYXplbmFkbyBubyAiY2xvbmUiCiAgICAgICAgICAgIC8vIHRhbWFuaG8gZG8gY29kaWdvIDB4MzcgKDU1IGJ5dGVzKQogICAgICAgICAgICByZXN1bHQgOj0gY3JlYXRlKDAsIGNsb25lLCAweDM3KQogICAgICAgIH0KICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/endereco-de-contrato-pre-computado-com-create2.md b/src/exemplos/aplicacoes/endereco-de-contrato-pre-computado-com-create2.md index 000f20c..df5a1bf 100644 --- a/src/exemplos/aplicacoes/endereco-de-contrato-pre-computado-com-create2.md +++ b/src/exemplos/aplicacoes/endereco-de-contrato-pre-computado-com-create2.md @@ -1,23 +1,37 @@ # Endereço de Contrato pré-computado com Create2 -`Endereço de contrato pode ser pré-computado`, antes do contrato ser implantado usando `create2` +O endereço do contrato pode ser pré-calculado, antes que o contrato seja implantado, usando `create2` ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; contract Factory { + // Retorna o endereço do contrato recém-implantado + function deploy( + address _owner, + uint _foo, + bytes32 _salt + ) public payable returns (address) { + // Esta sintaxe é uma nova maneira de invocar create2 sem assembly, você só precisa passar salt + // https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2 + return address(new TestContract{salt: _salt}(_owner, _foo)); + } +} + +// Esta é a maneira mais antiga de fazer isso usando assembly +contract FactoryAssembly { event Deployed(address addr, uint salt); - // 1. Pega o código de bytes do contrato a ser implantado - // NOTA: _owner e _foo são argumentos do constructor TestContract's + // 1. Obtenha o bytecode do contrato a ser implantado + // NOTA: _owner e _foo são argumentos do constructor TestContract function getBytecode(address _owner, uint _foo) public pure returns (bytes memory) { bytes memory bytecode = type(TestContract).creationCode; return abi.encodePacked(bytecode, abi.encode(_owner, _foo)); } - // 2. Computa o endereço do contrato a ser implantado + // 2. Calcular o endereço do contrato a ser implantado // NOTA: _salt é um número aleatório usado para criar um endereço function getAddress(bytes memory bytecode, uint _salt) public @@ -28,14 +42,14 @@ contract Factory { abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode)) ); - // NOTA: lança os últimos 20 bytes do hash para o endereço + // NOTA: converta os últimos 20 bytes de hash para o endereço return address(uint160(uint(hash))); } // 3. Implanta o contrato // NOTA: - // Verifica o evento log Deployed que contém o endereço do TestContract implantado. - // O endereço no log deve ser equivalente ao endereço computado de cima. + // Verifique o log de eventos Deployed que contém o endereço do TestContract implantado. + // O endereço no log deve ser igual ao endereço calculado acima. function deploy(bytes memory bytecode, uint _salt) public payable { address addr; @@ -47,14 +61,14 @@ contract Factory { e envia v wei e retorna o novo endereço onde novo endereço = primeiros 20 bytes de keccak256(0xff + address(this) + s + keccak256(mem[p…(p+n))) - s = valor big-endian 256-bit + s = valor big-endian 256-bit */ assembly { addr := create2( callvalue(), // wei enviado com a chamada atual - // O código real começa depois de saltar os primeiros 32 bytes + //O código real começa após pular os primeiros 32 bytes add(bytecode, 0x20), - mload(bytecode), // Carrega o tamanho do código contido nos primeiros 32 bytes + mload(bytecode), // Carregar o tamanho do código contido nos primeiros 32 bytes _salt // Salt dos argumentos de função ) @@ -81,3 +95,7 @@ contract TestContract { } } ``` + +## Teste no Remix + +- [Create2.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IEZhY3RvcnkgewogICAgLy8gUmV0b3JuYSBvIGVuZGVyZWNvIGRvIGNvbnRyYXRvIHJlY2VtLWltcGxhbnRhZG8KICAgIGZ1bmN0aW9uIGRlcGxveSgKICAgICAgICBhZGRyZXNzIF9vd25lciwKICAgICAgICB1aW50IF9mb28sCiAgICAgICAgYnl0ZXMzMiBfc2FsdAogICAgKSBwdWJsaWMgcGF5YWJsZSByZXR1cm5zIChhZGRyZXNzKSB7CiAgICAgICAgLy8gRXN0YSBzaW50YXhlIGUgdW1hIG5vdmEgbWFuZWlyYSBkZSBpbnZvY2FyIGNyZWF0ZTIgc2VtIGFzc2VtYmx5LCB2b2NlIHNvIHByZWNpc2EgcGFzc2FyIHNhbHQKICAgICAgICAvLyBodHRwczovL2RvY3Muc29saWRpdHlsYW5nLm9yZy9lbi9sYXRlc3QvY29udHJvbC1zdHJ1Y3R1cmVzLmh0bWwjc2FsdGVkLWNvbnRyYWN0LWNyZWF0aW9ucy1jcmVhdGUyCiAgICAgICAgcmV0dXJuIGFkZHJlc3MobmV3IFRlc3RDb250cmFjdHtzYWx0OiBfc2FsdH0oX293bmVyLCBfZm9vKSk7CiAgICB9Cn0KCi8vIEVzdGEgZSBhIG1hbmVpcmEgbWFpcyBhbnRpZ2EgZGUgZmF6ZXIgaXNzbyB1c2FuZG8gYXNzZW1ibHkKY29udHJhY3QgRmFjdG9yeUFzc2VtYmx5IHsKICAgIGV2ZW50IERlcGxveWVkKGFkZHJlc3MgYWRkciwgdWludCBzYWx0KTsKCiAgICAvLyAxLiBPYnRlbmhhIG8gYnl0ZWNvZGUgZG8gY29udHJhdG8gYSBzZXIgaW1wbGFudGFkbyAKICAgIC8vIE5PVEE6IF9vd25lciBlIF9mb28gc2FvIGFyZ3VtZW50b3MgZG8gY29uc3RydWN0b3IgVGVzdENvbnRyYWN0CiAgICBmdW5jdGlvbiBnZXRCeXRlY29kZShhZGRyZXNzIF9vd25lciwgdWludCBfZm9vKSBwdWJsaWMgcHVyZSByZXR1cm5zIChieXRlcyBtZW1vcnkpIHsKICAgICAgICBieXRlcyBtZW1vcnkgYnl0ZWNvZGUgPSB0eXBlKFRlc3RDb250cmFjdCkuY3JlYXRpb25Db2RlOwoKICAgICAgICByZXR1cm4gYWJpLmVuY29kZVBhY2tlZChieXRlY29kZSwgYWJpLmVuY29kZShfb3duZXIsIF9mb28pKTsKICAgIH0KCiAgICAvLyAyLiBDYWxjdWxhciBvIGVuZGVyZWNvIGRvIGNvbnRyYXRvIGEgc2VyIGltcGxhbnRhZG8KICAgIC8vIE5PVEE6IF9zYWx0IGUgdW0gbnVtZXJvIGFsZWF0b3JpbyB1c2FkbyBwYXJhIGNyaWFyIHVtIGVuZGVyZWNvCiAgICBmdW5jdGlvbiBnZXRBZGRyZXNzKGJ5dGVzIG1lbW9yeSBieXRlY29kZSwgdWludCBfc2FsdCkKICAgICAgICBwdWJsaWMKICAgICAgICB2aWV3CiAgICAgICAgcmV0dXJucyAoYWRkcmVzcykKICAgIHsKICAgICAgICBieXRlczMyIGhhc2ggPSBrZWNjYWsyNTYoCiAgICAgICAgICAgIGFiaS5lbmNvZGVQYWNrZWQoYnl0ZXMxKDB4ZmYpLCBhZGRyZXNzKHRoaXMpLCBfc2FsdCwga2VjY2FrMjU2KGJ5dGVjb2RlKSkKICAgICAgICApOwoKICAgICAgICAvLyBOT1RBOiBjb252ZXJ0YSBvcyB1bHRpbW9zIDIwIGJ5dGVzIGRlIGhhc2ggcGFyYSBvIGVuZGVyZWNvCiAgICAgICAgcmV0dXJuIGFkZHJlc3ModWludDE2MCh1aW50KGhhc2gpKSk7CiAgICB9CgogICAgLy8gMy4gSW1wbGFudGEgbyBjb250cmF0bwogICAgLy8gTk9UQToKICAgIC8vIFZlcmlmaXF1ZSBvIGxvZyBkZSBldmVudG9zIERlcGxveWVkIHF1ZSBjb250ZW0gbyBlbmRlcmVjbyBkbyBUZXN0Q29udHJhY3QgaW1wbGFudGFkby4KICAgIC8vIE8gZW5kZXJlY28gbm8gbG9nIGRldmUgc2VyIGlndWFsIGFvIGVuZGVyZWNvIGNhbGN1bGFkbyBhY2ltYS4KICAgIGZ1bmN0aW9uIGRlcGxveShieXRlcyBtZW1vcnkgYnl0ZWNvZGUsIHVpbnQgX3NhbHQpIHB1YmxpYyBwYXlhYmxlIHsKICAgICAgICBhZGRyZXNzIGFkZHI7CgogICAgICAgIC8qCiAgICAgICAgTk9UQTogQ29tbyBjcmlhciBjcmVhdGUyCgogICAgICAgIGNyZWF0ZTIodiwgcCwgbiwgcykKICAgICAgICBjcmlhIHVtIG5vdm8gY29udHJhdG8gY29tIGNvZGlnbyBuYSBtZW1vcmlhIHAgcGFyYSBwICsgbgogICAgICAgIGUgZW52aWEgdiB3ZWkKICAgICAgICBlIHJldG9ybmEgbyBub3ZvIGVuZGVyZWNvCiAgICAgICAgb25kZSBub3ZvIGVuZGVyZWNvID0gcHJpbWVpcm9zIDIwIGJ5dGVzIGRlIGtlY2NhazI1NigweGZmICsgYWRkcmVzcyh0aGlzKSArIHMgKyBrZWNjYWsyNTYobWVtW3A/KHArbikpKQogICAgICAgICAgICAgIHMgPSB2YWxvciBiaWctZW5kaWFuIDI1Ni1iaXQKICAgICAgICAqLwogICAgICAgIGFzc2VtYmx5IHsKICAgICAgICAgICAgYWRkciA6PSBjcmVhdGUyKAogICAgICAgICAgICAgICAgY2FsbHZhbHVlKCksIC8vIHdlaSBlbnZpYWRvIGNvbSBhIGNoYW1hZGEgYXR1YWwKICAgICAgICAgICAgICAgIC8vTyBjb2RpZ28gcmVhbCBjb21lY2EgYXBvcyBwdWxhciBvcyBwcmltZWlyb3MgMzIgYnl0ZXMKICAgICAgICAgICAgICAgIGFkZChieXRlY29kZSwgMHgyMCksCiAgICAgICAgICAgICAgICBtbG9hZChieXRlY29kZSksIC8vIENhcnJlZ2FyIG8gdGFtYW5obyBkbyBjb2RpZ28gY29udGlkbyBub3MgcHJpbWVpcm9zIDMyIGJ5dGVzCiAgICAgICAgICAgICAgICBfc2FsdCAvLyBTYWx0IGRvcyBhcmd1bWVudG9zIGRlIGZ1bmNhbwogICAgICAgICAgICApCgogICAgICAgICAgICBpZiBpc3plcm8oZXh0Y29kZXNpemUoYWRkcikpIHsKICAgICAgICAgICAgICAgIHJldmVydCgwLCAwKQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBlbWl0IERlcGxveWVkKGFkZHIsIF9zYWx0KTsKICAgIH0KfQoKY29udHJhY3QgVGVzdENvbnRyYWN0IHsKICAgIGFkZHJlc3MgcHVibGljIG93bmVyOwogICAgdWludCBwdWJsaWMgZm9vOwoKICAgIGNvbnN0cnVjdG9yKGFkZHJlc3MgX293bmVyLCB1aW50IF9mb28pIHBheWFibGUgewogICAgICAgIG93bmVyID0gX293bmVyOwogICAgICAgIGZvbyA9IF9mb287CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0QmFsYW5jZSgpIHB1YmxpYyB2aWV3IHJldHVybnMgKHVpbnQpIHsKICAgICAgICByZXR1cm4gYWRkcmVzcyh0aGlzKS5iYWxhbmNlOwogICAgfQp9&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/erc1155.md b/src/exemplos/aplicacoes/erc1155.md new file mode 100644 index 0000000..cd4afe1 --- /dev/null +++ b/src/exemplos/aplicacoes/erc1155.md @@ -0,0 +1,282 @@ +# ERC1155 + +Exemplo de um contrato ERC1155. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IERC1155 { + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 value, + bytes calldata data + ) external; + + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external; + + function balanceOf(address owner, uint256 id) external view returns (uint256); + + function balanceOfBatch( + address[] calldata owners, + uint256[] calldata ids + ) external view returns (uint256[] memory); + + function setApprovalForAll(address operator, bool approved) external; + + function isApprovedForAll( + address owner, + address operator + ) external view returns (bool); +} + +interface IERC1155TokenReceiver { + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external returns (bytes4); + + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external returns (bytes4); +} + +contract ERC1155 is IERC1155 { + event TransferSingle( + address indexed operator, + address indexed from, + address indexed to, + uint256 id, + uint256 value + ); + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] values + ); + event ApprovalForAll( + address indexed owner, + address indexed operator, + bool approved + ); + event URI(string value, uint256 indexed id); + + // owner => id => balance + mapping(address => mapping(uint256 => uint256)) public balanceOf; + // owner => operator => approved + mapping(address => mapping(address => bool)) public isApprovedForAll; + + function balanceOfBatch( + address[] calldata owners, + uint256[] calldata ids + ) external view returns (uint256[] memory balances) { + require(owners.length == ids.length, "owners length != ids length"); + + balances = new uint[](owners.length); + + unchecked { + for (uint256 i = 0; i < owners.length; i++) { + balances[i] = balanceOf[owners[i]][ids[i]]; + } + } + } + + function setApprovalForAll(address operator, bool approved) external { + isApprovedForAll[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 value, + bytes calldata data + ) external { + require( + msg.sender == from || isApprovedForAll[from][msg.sender], + "not approved" + ); + require(to != address(0), "to = 0 address"); + + balanceOf[from][id] -= value; + balanceOf[to][id] += value; + + emit TransferSingle(msg.sender, from, to, id, value); + + if (to.code.length > 0) { + require( + IERC1155TokenReceiver(to).onERC1155Received( + msg.sender, + from, + id, + value, + data + ) == IERC1155TokenReceiver.onERC1155Received.selector, + "unsafe transfer" + ); + } + } + + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external { + require( + msg.sender == from || isApprovedForAll[from][msg.sender], + "not approved" + ); + require(to != address(0), "to = 0 address"); + require(ids.length == values.length, "ids length != values length"); + + for (uint256 i = 0; i < ids.length; i++) { + balanceOf[from][ids[i]] -= values[i]; + balanceOf[to][ids[i]] += values[i]; + } + + emit TransferBatch(msg.sender, from, to, ids, values); + + if (to.code.length > 0) { + require( + IERC1155TokenReceiver(to).onERC1155BatchReceived( + msg.sender, + from, + ids, + values, + data + ) == IERC1155TokenReceiver.onERC1155BatchReceived.selector, + "unsafe transfer" + ); + } + } + + // ERC165 + function supportsInterface(bytes4 interfaceId) external view returns (bool) { + return + interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 + interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 + interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI + } + + // ERC1155 Metadata URI + function uri(uint256 id) public view virtual returns (string memory) {} + + // Internal functions + function _mint(address to, uint256 id, uint256 value, bytes memory data) internal { + require(to != address(0), "to = 0 address"); + + balanceOf[to][id] += value; + + emit TransferSingle(msg.sender, address(0), to, id, value); + + if (to.code.length > 0) { + require( + IERC1155TokenReceiver(to).onERC1155Received( + msg.sender, + address(0), + id, + value, + data + ) == IERC1155TokenReceiver.onERC1155Received.selector, + "unsafe transfer" + ); + } + } + + function _batchMint( + address to, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) internal { + require(to != address(0), "to = 0 address"); + require(ids.length == values.length, "ids length != values length"); + + for (uint256 i = 0; i < ids.length; i++) { + balanceOf[to][ids[i]] += values[i]; + } + + emit TransferBatch(msg.sender, address(0), to, ids, values); + + if (to.code.length > 0) { + require( + IERC1155TokenReceiver(to).onERC1155BatchReceived( + msg.sender, + address(0), + ids, + values, + data + ) == IERC1155TokenReceiver.onERC1155BatchReceived.selector, + "unsafe transfer" + ); + } + } + + function _burn(address from, uint256 id, uint256 value) internal { + require(from != address(0), "from = 0 address"); + balanceOf[from][id] -= value; + emit TransferSingle(msg.sender, from, address(0), id, value); + } + + function _batchBurn( + address from, + uint256[] calldata ids, + uint256[] calldata values + ) internal { + require(from != address(0), "from = 0 address"); + require(ids.length == values.length, "ids length != values length"); + + for (uint256 i = 0; i < ids.length; i++) { + balanceOf[from][ids[i]] -= values[i]; + } + + emit TransferBatch(msg.sender, from, address(0), ids, values); + } +} + +contract MyMultiToken is ERC1155 { + function mint(uint256 id, uint256 value, bytes memory data) external { + _mint(msg.sender, id, value, data); + } + + function batchMint( + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external { + _batchMint(msg.sender, ids, values, data); + } + + function burn(uint256 id, uint256 value) external { + _burn(msg.sender, id, value); + } + + function batchBurn(uint256[] calldata ids, uint256[] calldata values) external { + _batchBurn(msg.sender, ids, values); + } +} +``` + +## Teste no Remix + +- [ERC1155.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmludGVyZmFjZSBJRVJDMTE1NSB7CiAgICBmdW5jdGlvbiBzYWZlVHJhbnNmZXJGcm9tKAogICAgICAgIGFkZHJlc3MgZnJvbSwKICAgICAgICBhZGRyZXNzIHRvLAogICAgICAgIHVpbnQyNTYgaWQsCiAgICAgICAgdWludDI1NiB2YWx1ZSwKICAgICAgICBieXRlcyBjYWxsZGF0YSBkYXRhCiAgICApIGV4dGVybmFsOwoKICAgIGZ1bmN0aW9uIHNhZmVCYXRjaFRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzIGZyb20sCiAgICAgICAgYWRkcmVzcyB0bywKICAgICAgICB1aW50MjU2W10gY2FsbGRhdGEgaWRzLAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSB2YWx1ZXMsCiAgICAgICAgYnl0ZXMgY2FsbGRhdGEgZGF0YQogICAgKSBleHRlcm5hbDsKCiAgICBmdW5jdGlvbiBiYWxhbmNlT2YoYWRkcmVzcyBvd25lciwgdWludDI1NiBpZCkgZXh0ZXJuYWwgdmlldyByZXR1cm5zICh1aW50MjU2KTsKCiAgICBmdW5jdGlvbiBiYWxhbmNlT2ZCYXRjaCgKICAgICAgICBhZGRyZXNzW10gY2FsbGRhdGEgb3duZXJzLAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSBpZHMKICAgICkgZXh0ZXJuYWwgdmlldyByZXR1cm5zICh1aW50MjU2W10gbWVtb3J5KTsKCiAgICBmdW5jdGlvbiBzZXRBcHByb3ZhbEZvckFsbChhZGRyZXNzIG9wZXJhdG9yLCBib29sIGFwcHJvdmVkKSBleHRlcm5hbDsKCiAgICBmdW5jdGlvbiBpc0FwcHJvdmVkRm9yQWxsKAogICAgICAgIGFkZHJlc3Mgb3duZXIsCiAgICAgICAgYWRkcmVzcyBvcGVyYXRvcgogICAgKSBleHRlcm5hbCB2aWV3IHJldHVybnMgKGJvb2wpOwp9CgppbnRlcmZhY2UgSUVSQzExNTVUb2tlblJlY2VpdmVyIHsKICAgIGZ1bmN0aW9uIG9uRVJDMTE1NVJlY2VpdmVkKAogICAgICAgIGFkZHJlc3Mgb3BlcmF0b3IsCiAgICAgICAgYWRkcmVzcyBmcm9tLAogICAgICAgIHVpbnQyNTYgaWQsCiAgICAgICAgdWludDI1NiB2YWx1ZSwKICAgICAgICBieXRlcyBjYWxsZGF0YSBkYXRhCiAgICApIGV4dGVybmFsIHJldHVybnMgKGJ5dGVzNCk7CgogICAgZnVuY3Rpb24gb25FUkMxMTU1QmF0Y2hSZWNlaXZlZCgKICAgICAgICBhZGRyZXNzIG9wZXJhdG9yLAogICAgICAgIGFkZHJlc3MgZnJvbSwKICAgICAgICB1aW50MjU2W10gY2FsbGRhdGEgaWRzLAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSB2YWx1ZXMsCiAgICAgICAgYnl0ZXMgY2FsbGRhdGEgZGF0YQogICAgKSBleHRlcm5hbCByZXR1cm5zIChieXRlczQpOwp9Cgpjb250cmFjdCBFUkMxMTU1IGlzIElFUkMxMTU1IHsKICAgIGV2ZW50IFRyYW5zZmVyU2luZ2xlKAogICAgICAgIGFkZHJlc3MgaW5kZXhlZCBvcGVyYXRvciwKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgZnJvbSwKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgdG8sCiAgICAgICAgdWludDI1NiBpZCwKICAgICAgICB1aW50MjU2IHZhbHVlCiAgICApOwogICAgZXZlbnQgVHJhbnNmZXJCYXRjaCgKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgb3BlcmF0b3IsCiAgICAgICAgYWRkcmVzcyBpbmRleGVkIGZyb20sCiAgICAgICAgYWRkcmVzcyBpbmRleGVkIHRvLAogICAgICAgIHVpbnQyNTZbXSBpZHMsCiAgICAgICAgdWludDI1NltdIHZhbHVlcwogICAgKTsKICAgIGV2ZW50IEFwcHJvdmFsRm9yQWxsKAogICAgICAgIGFkZHJlc3MgaW5kZXhlZCBvd25lciwKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgb3BlcmF0b3IsCiAgICAgICAgYm9vbCBhcHByb3ZlZAogICAgKTsKICAgIGV2ZW50IFVSSShzdHJpbmcgdmFsdWUsIHVpbnQyNTYgaW5kZXhlZCBpZCk7CgogICAgLy8gb3duZXIgPT4gaWQgPT4gYmFsYW5jZQogICAgbWFwcGluZyhhZGRyZXNzID0+IG1hcHBpbmcodWludDI1NiA9PiB1aW50MjU2KSkgcHVibGljIGJhbGFuY2VPZjsKICAgIC8vIG93bmVyID0+IG9wZXJhdG9yID0+IGFwcHJvdmVkCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gbWFwcGluZyhhZGRyZXNzID0+IGJvb2wpKSBwdWJsaWMgaXNBcHByb3ZlZEZvckFsbDsKCiAgICBmdW5jdGlvbiBiYWxhbmNlT2ZCYXRjaCgKICAgICAgICBhZGRyZXNzW10gY2FsbGRhdGEgb3duZXJzLAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSBpZHMKICAgICkgZXh0ZXJuYWwgdmlldyByZXR1cm5zICh1aW50MjU2W10gbWVtb3J5IGJhbGFuY2VzKSB7CiAgICAgICAgcmVxdWlyZShvd25lcnMubGVuZ3RoID09IGlkcy5sZW5ndGgsICJvd25lcnMgbGVuZ3RoICE9IGlkcyBsZW5ndGgiKTsKCiAgICAgICAgYmFsYW5jZXMgPSBuZXcgdWludFtdKG93bmVycy5sZW5ndGgpOwoKICAgICAgICB1bmNoZWNrZWQgewogICAgICAgICAgICBmb3IgKHVpbnQyNTYgaSA9IDA7IGkgPCBvd25lcnMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgICAgIGJhbGFuY2VzW2ldID0gYmFsYW5jZU9mW293bmVyc1tpXV1baWRzW2ldXTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBzZXRBcHByb3ZhbEZvckFsbChhZGRyZXNzIG9wZXJhdG9yLCBib29sIGFwcHJvdmVkKSBleHRlcm5hbCB7CiAgICAgICAgaXNBcHByb3ZlZEZvckFsbFttc2cuc2VuZGVyXVtvcGVyYXRvcl0gPSBhcHByb3ZlZDsKICAgICAgICBlbWl0IEFwcHJvdmFsRm9yQWxsKG1zZy5zZW5kZXIsIG9wZXJhdG9yLCBhcHByb3ZlZCk7CiAgICB9CgogICAgZnVuY3Rpb24gc2FmZVRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzIGZyb20sCiAgICAgICAgYWRkcmVzcyB0bywKICAgICAgICB1aW50MjU2IGlkLAogICAgICAgIHVpbnQyNTYgdmFsdWUsCiAgICAgICAgYnl0ZXMgY2FsbGRhdGEgZGF0YQogICAgKSBleHRlcm5hbCB7CiAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgbXNnLnNlbmRlciA9PSBmcm9tIHx8IGlzQXBwcm92ZWRGb3JBbGxbZnJvbV1bbXNnLnNlbmRlcl0sCiAgICAgICAgICAgICJub3QgYXBwcm92ZWQiCiAgICAgICAgKTsKICAgICAgICByZXF1aXJlKHRvICE9IGFkZHJlc3MoMCksICJ0byA9IDAgYWRkcmVzcyIpOwoKICAgICAgICBiYWxhbmNlT2ZbZnJvbV1baWRdIC09IHZhbHVlOwogICAgICAgIGJhbGFuY2VPZlt0b11baWRdICs9IHZhbHVlOwoKICAgICAgICBlbWl0IFRyYW5zZmVyU2luZ2xlKG1zZy5zZW5kZXIsIGZyb20sIHRvLCBpZCwgdmFsdWUpOwoKICAgICAgICBpZiAodG8uY29kZS5sZW5ndGggPiAwKSB7CiAgICAgICAgICAgIHJlcXVpcmUoCiAgICAgICAgICAgICAgICBJRVJDMTE1NVRva2VuUmVjZWl2ZXIodG8pLm9uRVJDMTE1NVJlY2VpdmVkKAogICAgICAgICAgICAgICAgICAgIG1zZy5zZW5kZXIsCiAgICAgICAgICAgICAgICAgICAgZnJvbSwKICAgICAgICAgICAgICAgICAgICBpZCwKICAgICAgICAgICAgICAgICAgICB2YWx1ZSwKICAgICAgICAgICAgICAgICAgICBkYXRhCiAgICAgICAgICAgICAgICApID09IElFUkMxMTU1VG9rZW5SZWNlaXZlci5vbkVSQzExNTVSZWNlaXZlZC5zZWxlY3RvciwKICAgICAgICAgICAgICAgICJ1bnNhZmUgdHJhbnNmZXIiCiAgICAgICAgICAgICk7CiAgICAgICAgfQogICAgfQoKICAgIGZ1bmN0aW9uIHNhZmVCYXRjaFRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzIGZyb20sCiAgICAgICAgYWRkcmVzcyB0bywKICAgICAgICB1aW50MjU2W10gY2FsbGRhdGEgaWRzLAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSB2YWx1ZXMsCiAgICAgICAgYnl0ZXMgY2FsbGRhdGEgZGF0YQogICAgKSBleHRlcm5hbCB7CiAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgbXNnLnNlbmRlciA9PSBmcm9tIHx8IGlzQXBwcm92ZWRGb3JBbGxbZnJvbV1bbXNnLnNlbmRlcl0sCiAgICAgICAgICAgICJub3QgYXBwcm92ZWQiCiAgICAgICAgKTsKICAgICAgICByZXF1aXJlKHRvICE9IGFkZHJlc3MoMCksICJ0byA9IDAgYWRkcmVzcyIpOwogICAgICAgIHJlcXVpcmUoaWRzLmxlbmd0aCA9PSB2YWx1ZXMubGVuZ3RoLCAiaWRzIGxlbmd0aCAhPSB2YWx1ZXMgbGVuZ3RoIik7CgogICAgICAgIGZvciAodWludDI1NiBpID0gMDsgaSA8IGlkcy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICBiYWxhbmNlT2ZbZnJvbV1baWRzW2ldXSAtPSB2YWx1ZXNbaV07CiAgICAgICAgICAgIGJhbGFuY2VPZlt0b11baWRzW2ldXSArPSB2YWx1ZXNbaV07CiAgICAgICAgfQoKICAgICAgICBlbWl0IFRyYW5zZmVyQmF0Y2gobXNnLnNlbmRlciwgZnJvbSwgdG8sIGlkcywgdmFsdWVzKTsKCiAgICAgICAgaWYgKHRvLmNvZGUubGVuZ3RoID4gMCkgewogICAgICAgICAgICByZXF1aXJlKAogICAgICAgICAgICAgICAgSUVSQzExNTVUb2tlblJlY2VpdmVyKHRvKS5vbkVSQzExNTVCYXRjaFJlY2VpdmVkKAogICAgICAgICAgICAgICAgICAgIG1zZy5zZW5kZXIsCiAgICAgICAgICAgICAgICAgICAgZnJvbSwKICAgICAgICAgICAgICAgICAgICBpZHMsCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzLAogICAgICAgICAgICAgICAgICAgIGRhdGEKICAgICAgICAgICAgICAgICkgPT0gSUVSQzExNTVUb2tlblJlY2VpdmVyLm9uRVJDMTE1NUJhdGNoUmVjZWl2ZWQuc2VsZWN0b3IsCiAgICAgICAgICAgICAgICAidW5zYWZlIHRyYW5zZmVyIgogICAgICAgICAgICApOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBFUkMxNjUKICAgIGZ1bmN0aW9uIHN1cHBvcnRzSW50ZXJmYWNlKGJ5dGVzNCBpbnRlcmZhY2VJZCkgZXh0ZXJuYWwgdmlldyByZXR1cm5zIChib29sKSB7CiAgICAgICAgcmV0dXJuCiAgICAgICAgICAgIGludGVyZmFjZUlkID09IDB4MDFmZmM5YTcgfHwgLy8gRVJDMTY1IEludGVyZmFjZSBJRCBmb3IgRVJDMTY1CiAgICAgICAgICAgIGludGVyZmFjZUlkID09IDB4ZDliNjdhMjYgfHwgLy8gRVJDMTY1IEludGVyZmFjZSBJRCBmb3IgRVJDMTE1NQogICAgICAgICAgICBpbnRlcmZhY2VJZCA9PSAweDBlODkzNDFjOyAvLyBFUkMxNjUgSW50ZXJmYWNlIElEIGZvciBFUkMxMTU1TWV0YWRhdGFVUkkKICAgIH0KCiAgICAvLyBFUkMxMTU1IE1ldGFkYXRhIFVSSQogICAgZnVuY3Rpb24gdXJpKHVpbnQyNTYgaWQpIHB1YmxpYyB2aWV3IHZpcnR1YWwgcmV0dXJucyAoc3RyaW5nIG1lbW9yeSkge30KCiAgICAvLyBJbnRlcm5hbCBmdW5jdGlvbnMKICAgIGZ1bmN0aW9uIF9taW50KGFkZHJlc3MgdG8sIHVpbnQyNTYgaWQsIHVpbnQyNTYgdmFsdWUsIGJ5dGVzIG1lbW9yeSBkYXRhKSBpbnRlcm5hbCB7CiAgICAgICAgcmVxdWlyZSh0byAhPSBhZGRyZXNzKDApLCAidG8gPSAwIGFkZHJlc3MiKTsKCiAgICAgICAgYmFsYW5jZU9mW3RvXVtpZF0gKz0gdmFsdWU7CgogICAgICAgIGVtaXQgVHJhbnNmZXJTaW5nbGUobXNnLnNlbmRlciwgYWRkcmVzcygwKSwgdG8sIGlkLCB2YWx1ZSk7CgogICAgICAgIGlmICh0by5jb2RlLmxlbmd0aCA+IDApIHsKICAgICAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgICAgIElFUkMxMTU1VG9rZW5SZWNlaXZlcih0bykub25FUkMxMTU1UmVjZWl2ZWQoCiAgICAgICAgICAgICAgICAgICAgbXNnLnNlbmRlciwKICAgICAgICAgICAgICAgICAgICBhZGRyZXNzKDApLAogICAgICAgICAgICAgICAgICAgIGlkLAogICAgICAgICAgICAgICAgICAgIHZhbHVlLAogICAgICAgICAgICAgICAgICAgIGRhdGEKICAgICAgICAgICAgICAgICkgPT0gSUVSQzExNTVUb2tlblJlY2VpdmVyLm9uRVJDMTE1NVJlY2VpdmVkLnNlbGVjdG9yLAogICAgICAgICAgICAgICAgInVuc2FmZSB0cmFuc2ZlciIKICAgICAgICAgICAgKTsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gX2JhdGNoTWludCgKICAgICAgICBhZGRyZXNzIHRvLAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSBpZHMsCiAgICAgICAgdWludDI1NltdIGNhbGxkYXRhIHZhbHVlcywKICAgICAgICBieXRlcyBjYWxsZGF0YSBkYXRhCiAgICApIGludGVybmFsIHsKICAgICAgICByZXF1aXJlKHRvICE9IGFkZHJlc3MoMCksICJ0byA9IDAgYWRkcmVzcyIpOwogICAgICAgIHJlcXVpcmUoaWRzLmxlbmd0aCA9PSB2YWx1ZXMubGVuZ3RoLCAiaWRzIGxlbmd0aCAhPSB2YWx1ZXMgbGVuZ3RoIik7CgogICAgICAgIGZvciAodWludDI1NiBpID0gMDsgaSA8IGlkcy5sZW5ndGg7IGkrKykgewogICAgICAgICAgICBiYWxhbmNlT2ZbdG9dW2lkc1tpXV0gKz0gdmFsdWVzW2ldOwogICAgICAgIH0KCiAgICAgICAgZW1pdCBUcmFuc2ZlckJhdGNoKG1zZy5zZW5kZXIsIGFkZHJlc3MoMCksIHRvLCBpZHMsIHZhbHVlcyk7CgogICAgICAgIGlmICh0by5jb2RlLmxlbmd0aCA+IDApIHsKICAgICAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgICAgIElFUkMxMTU1VG9rZW5SZWNlaXZlcih0bykub25FUkMxMTU1QmF0Y2hSZWNlaXZlZCgKICAgICAgICAgICAgICAgICAgICBtc2cuc2VuZGVyLAogICAgICAgICAgICAgICAgICAgIGFkZHJlc3MoMCksCiAgICAgICAgICAgICAgICAgICAgaWRzLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcywKICAgICAgICAgICAgICAgICAgICBkYXRhCiAgICAgICAgICAgICAgICApID09IElFUkMxMTU1VG9rZW5SZWNlaXZlci5vbkVSQzExNTVCYXRjaFJlY2VpdmVkLnNlbGVjdG9yLAogICAgICAgICAgICAgICAgInVuc2FmZSB0cmFuc2ZlciIKICAgICAgICAgICAgKTsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gX2J1cm4oYWRkcmVzcyBmcm9tLCB1aW50MjU2IGlkLCB1aW50MjU2IHZhbHVlKSBpbnRlcm5hbCB7CiAgICAgICAgcmVxdWlyZShmcm9tICE9IGFkZHJlc3MoMCksICJmcm9tID0gMCBhZGRyZXNzIik7CiAgICAgICAgYmFsYW5jZU9mW2Zyb21dW2lkXSAtPSB2YWx1ZTsKICAgICAgICBlbWl0IFRyYW5zZmVyU2luZ2xlKG1zZy5zZW5kZXIsIGZyb20sIGFkZHJlc3MoMCksIGlkLCB2YWx1ZSk7CiAgICB9CgogICAgZnVuY3Rpb24gX2JhdGNoQnVybigKICAgICAgICBhZGRyZXNzIGZyb20sCiAgICAgICAgdWludDI1NltdIGNhbGxkYXRhIGlkcywKICAgICAgICB1aW50MjU2W10gY2FsbGRhdGEgdmFsdWVzCiAgICApIGludGVybmFsIHsKICAgICAgICByZXF1aXJlKGZyb20gIT0gYWRkcmVzcygwKSwgImZyb20gPSAwIGFkZHJlc3MiKTsKICAgICAgICByZXF1aXJlKGlkcy5sZW5ndGggPT0gdmFsdWVzLmxlbmd0aCwgImlkcyBsZW5ndGggIT0gdmFsdWVzIGxlbmd0aCIpOwoKICAgICAgICBmb3IgKHVpbnQyNTYgaSA9IDA7IGkgPCBpZHMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgYmFsYW5jZU9mW2Zyb21dW2lkc1tpXV0gLT0gdmFsdWVzW2ldOwogICAgICAgIH0KCiAgICAgICAgZW1pdCBUcmFuc2ZlckJhdGNoKG1zZy5zZW5kZXIsIGZyb20sIGFkZHJlc3MoMCksIGlkcywgdmFsdWVzKTsKICAgIH0KfQoKY29udHJhY3QgTXlNdWx0aVRva2VuIGlzIEVSQzExNTUgewogICAgZnVuY3Rpb24gbWludCh1aW50MjU2IGlkLCB1aW50MjU2IHZhbHVlLCBieXRlcyBtZW1vcnkgZGF0YSkgZXh0ZXJuYWwgewogICAgICAgIF9taW50KG1zZy5zZW5kZXIsIGlkLCB2YWx1ZSwgZGF0YSk7CiAgICB9CgogICAgZnVuY3Rpb24gYmF0Y2hNaW50KAogICAgICAgIHVpbnQyNTZbXSBjYWxsZGF0YSBpZHMsCiAgICAgICAgdWludDI1NltdIGNhbGxkYXRhIHZhbHVlcywKICAgICAgICBieXRlcyBjYWxsZGF0YSBkYXRhCiAgICApIGV4dGVybmFsIHsKICAgICAgICBfYmF0Y2hNaW50KG1zZy5zZW5kZXIsIGlkcywgdmFsdWVzLCBkYXRhKTsKICAgIH0KCiAgICBmdW5jdGlvbiBidXJuKHVpbnQyNTYgaWQsIHVpbnQyNTYgdmFsdWUpIGV4dGVybmFsIHsKICAgICAgICBfYnVybihtc2cuc2VuZGVyLCBpZCwgdmFsdWUpOwogICAgfQoKICAgIGZ1bmN0aW9uIGJhdGNoQnVybih1aW50MjU2W10gY2FsbGRhdGEgaWRzLCB1aW50MjU2W10gY2FsbGRhdGEgdmFsdWVzKSBleHRlcm5hbCB7CiAgICAgICAgX2JhdGNoQnVybihtc2cuc2VuZGVyLCBpZHMsIHZhbHVlcyk7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/erc20.md b/src/exemplos/aplicacoes/erc20.md index a9d9f0d..137aacf 100644 --- a/src/exemplos/aplicacoes/erc20.md +++ b/src/exemplos/aplicacoes/erc20.md @@ -1,17 +1,17 @@ # ERC20 -`Todo contrato que segue o` [ERC20 standard](https://eips.ethereum.org/EIPS/eip-20) é um token ERC20. +Todo contrato que segue o [ERC20 standard](https://eips.ethereum.org/EIPS/eip-20) é um token ERC20. Tokens ERC20 fornecem funcionalidades para -* transferir tokens -* permitir que outros transfiram tokens em nome do titular do token +- transferir tokens +- permitir que outros transfiram tokens em nome do titular do token Eis aqui a interface para ERC20. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol interface IERC20 { @@ -26,9 +26,9 @@ interface IERC20 { function approve(address spender, uint amount) external returns (bool); function transferFrom( - address sender, - address recipient, - uint amount + address remetente, + address destinatario, + uint quantidade ) external returns (bool); event Transfer(address indexed from, address indexed to, uint value); @@ -36,6 +36,61 @@ interface IERC20 { } ``` +Exemplo de um contrato de token ERC20. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "./IERC20.sol"; + +contract ERC20 is IERC20 { + uint public totalSupply; + mapping(address => uint) public balanceOf; + mapping(address => mapping(address => uint)) public allowance; + string public name = "Solidity by Example"; + string public symbol = "SOLBYEX"; + uint8 public decimals = 18; + + function transfer(address recipient, uint amount) external returns (bool) { + balanceOf[msg.sender] -= amount; + balanceOf[recipient] += amount; + emit Transfer(msg.sender, recipient, amount); + return true; + } + + function approve(address spender, uint amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address remetente, + address destinatario, + uint quantidade + ) external returns (bool) { + allowance[sender][msg.sender] -= amount; + balanceOf[sender] -= amount; + balanceOf[recipient] += amount; + emit Transfer(sender, recipient, amount); + return true; + } + + function mint(uint amount) external { + balanceOf[msg.sender] += amount; + totalSupply += amount; + emit Transfer(address(0), msg.sender, amount); + } + + function burn(uint amount) external { + balanceOf[msg.sender] -= amount; + totalSupply -= amount; + emit Transfer(msg.sender, address(0), amount); + } +} +``` + ### Crie seu próprio token ERC20 Usando [Open Zeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) é muito fácil criar seu próprio token ERC20. @@ -44,7 +99,7 @@ Eis aqui um exemplo ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol"; @@ -52,7 +107,7 @@ contract MyToken is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) { // Mint 100 tokens para msg.sender // Semelhante a como - // 1 dollar = 100 cents + // 1 real = 100 centavos // 1 token = 1 * (10 ** decimals) _mint(msg.sender, 100 * 10**uint(decimals())); } @@ -61,24 +116,26 @@ contract MyToken is ERC20 { ### Contrato para trocar tokens -Eis um exemplo de contrato, `TokenSwap`, para negociar token ERC20 por outro. +Aqui está um exemplo de contrato, `TokenSwap`, para trocar um token ERC20 por outro. Este contrato negociará tokens chamando -transferFrom(address sender, address recipient, uint256 amount) +```solidity +transferFrom(address remetente, address destinatario, uint256 quantidade) +``` -que irá transferir uma quantidade de token do remetente para o destinatário. +que irá transferir uma `quantidade` de token do `remetente` para o `destinatario`. -Para `transferFrom` suceder, o remetente deve +Para `transferFrom` ter sucesso, o remetente deve -* ter mais que o `amount` de tokens no seu saldo -* permitir `TokenSwap` para retirar o`amount` de tokens chamando `approve` +- A `quantidade` de tokens no seu saldo deve ser maior do que está enviando +- permitir `TokenSwap` para retirar uma `quantidade` de tokens chamando `approve` -antes do `TokenSwap`chamar `transferFrom` +antes do `TokenSwap` chamar `transferFrom` ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/IERC20.sol"; @@ -145,3 +202,10 @@ contract TokenSwap { } } ``` + +## Teste no Remix + +- [IERC20.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9PcGVuWmVwcGVsaW4vb3BlbnplcHBlbGluLWNvbnRyYWN0cy9ibG9iL3YzLjAuMC9jb250cmFjdHMvdG9rZW4vRVJDMjAvSUVSQzIwLnNvbAppbnRlcmZhY2UgSUVSQzIwIHsKICAgIGZ1bmN0aW9uIHRvdGFsU3VwcGx5KCkgZXh0ZXJuYWwgdmlldyByZXR1cm5zICh1aW50KTsKCiAgICBmdW5jdGlvbiBiYWxhbmNlT2YoYWRkcmVzcyBhY2NvdW50KSBleHRlcm5hbCB2aWV3IHJldHVybnMgKHVpbnQpOwoKICAgIGZ1bmN0aW9uIHRyYW5zZmVyKGFkZHJlc3MgcmVjaXBpZW50LCB1aW50IGFtb3VudCkgZXh0ZXJuYWwgcmV0dXJucyAoYm9vbCk7CgogICAgZnVuY3Rpb24gYWxsb3dhbmNlKGFkZHJlc3Mgb3duZXIsIGFkZHJlc3Mgc3BlbmRlcikgZXh0ZXJuYWwgdmlldyByZXR1cm5zICh1aW50KTsKCiAgICBmdW5jdGlvbiBhcHByb3ZlKGFkZHJlc3Mgc3BlbmRlciwgdWludCBhbW91bnQpIGV4dGVybmFsIHJldHVybnMgKGJvb2wpOwoKICAgIGZ1bmN0aW9uIHRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzIHJlbWV0ZW50ZSwKICAgICAgICBhZGRyZXNzIGRlc3RpbmF0YXJpbywKICAgICAgICB1aW50IHF1YW50aWRhZGUKICAgICkgZXh0ZXJuYWwgcmV0dXJucyAoYm9vbCk7CgogICAgZXZlbnQgVHJhbnNmZXIoYWRkcmVzcyBpbmRleGVkIGZyb20sIGFkZHJlc3MgaW5kZXhlZCB0bywgdWludCB2YWx1ZSk7CiAgICBldmVudCBBcHByb3ZhbChhZGRyZXNzIGluZGV4ZWQgb3duZXIsIGFkZHJlc3MgaW5kZXhlZCBzcGVuZGVyLCB1aW50IHZhbHVlKTsKfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) +- [ERC20.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmltcG9ydCAiLi9JRVJDMjAuc29sIjsKCmNvbnRyYWN0IEVSQzIwIGlzIElFUkMyMCB7CiAgICB1aW50IHB1YmxpYyB0b3RhbFN1cHBseTsKICAgIG1hcHBpbmcoYWRkcmVzcyA9PiB1aW50KSBwdWJsaWMgYmFsYW5jZU9mOwogICAgbWFwcGluZyhhZGRyZXNzID0+IG1hcHBpbmcoYWRkcmVzcyA9PiB1aW50KSkgcHVibGljIGFsbG93YW5jZTsKICAgIHN0cmluZyBwdWJsaWMgbmFtZSA9ICJTb2xpZGl0eSBieSBFeGFtcGxlIjsKICAgIHN0cmluZyBwdWJsaWMgc3ltYm9sID0gIlNPTEJZRVgiOwogICAgdWludDggcHVibGljIGRlY2ltYWxzID0gMTg7CgogICAgZnVuY3Rpb24gdHJhbnNmZXIoYWRkcmVzcyByZWNpcGllbnQsIHVpbnQgYW1vdW50KSBleHRlcm5hbCByZXR1cm5zIChib29sKSB7CiAgICAgICAgYmFsYW5jZU9mW21zZy5zZW5kZXJdIC09IGFtb3VudDsKICAgICAgICBiYWxhbmNlT2ZbcmVjaXBpZW50XSArPSBhbW91bnQ7CiAgICAgICAgZW1pdCBUcmFuc2Zlcihtc2cuc2VuZGVyLCByZWNpcGllbnQsIGFtb3VudCk7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgZnVuY3Rpb24gYXBwcm92ZShhZGRyZXNzIHNwZW5kZXIsIHVpbnQgYW1vdW50KSBleHRlcm5hbCByZXR1cm5zIChib29sKSB7CiAgICAgICAgYWxsb3dhbmNlW21zZy5zZW5kZXJdW3NwZW5kZXJdID0gYW1vdW50OwogICAgICAgIGVtaXQgQXBwcm92YWwobXNnLnNlbmRlciwgc3BlbmRlciwgYW1vdW50KTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBmdW5jdGlvbiB0cmFuc2ZlckZyb20oCiAgICAgICAgYWRkcmVzcyByZW1ldGVudGUsCiAgICAgICAgYWRkcmVzcyBkZXN0aW5hdGFyaW8sCiAgICAgICAgdWludCBxdWFudGlkYWRlCiAgICApIGV4dGVybmFsIHJldHVybnMgKGJvb2wpIHsKICAgICAgICBhbGxvd2FuY2Vbc2VuZGVyXVttc2cuc2VuZGVyXSAtPSBhbW91bnQ7CiAgICAgICAgYmFsYW5jZU9mW3NlbmRlcl0gLT0gYW1vdW50OwogICAgICAgIGJhbGFuY2VPZltyZWNpcGllbnRdICs9IGFtb3VudDsKICAgICAgICBlbWl0IFRyYW5zZmVyKHNlbmRlciwgcmVjaXBpZW50LCBhbW91bnQpOwogICAgICAgIHJldHVybiB0cnVlOwogICAgfQoKICAgIGZ1bmN0aW9uIG1pbnQodWludCBhbW91bnQpIGV4dGVybmFsIHsKICAgICAgICBiYWxhbmNlT2ZbbXNnLnNlbmRlcl0gKz0gYW1vdW50OwogICAgICAgIHRvdGFsU3VwcGx5ICs9IGFtb3VudDsKICAgICAgICBlbWl0IFRyYW5zZmVyKGFkZHJlc3MoMCksIG1zZy5zZW5kZXIsIGFtb3VudCk7CiAgICB9CgogICAgZnVuY3Rpb24gYnVybih1aW50IGFtb3VudCkgZXh0ZXJuYWwgewogICAgICAgIGJhbGFuY2VPZlttc2cuc2VuZGVyXSAtPSBhbW91bnQ7CiAgICAgICAgdG90YWxTdXBwbHkgLT0gYW1vdW50OwogICAgICAgIGVtaXQgVHJhbnNmZXIobXNnLnNlbmRlciwgYWRkcmVzcygwKSwgYW1vdW50KTsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) +- [MyToken.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmltcG9ydCAiaHR0cHM6Ly9naXRodWIuY29tL09wZW5aZXBwZWxpbi9vcGVuemVwcGVsaW4tY29udHJhY3RzL2Jsb2IvdjQuMC4wL2NvbnRyYWN0cy90b2tlbi9FUkMyMC9FUkMyMC5zb2wiOwoKY29udHJhY3QgTXlUb2tlbiBpcyBFUkMyMCB7CiAgICBjb25zdHJ1Y3RvcihzdHJpbmcgbWVtb3J5IG5hbWUsIHN0cmluZyBtZW1vcnkgc3ltYm9sKSBFUkMyMChuYW1lLCBzeW1ib2wpIHsKICAgICAgICAvLyBNaW50IDEwMCB0b2tlbnMgcGFyYSBtc2cuc2VuZGVyCiAgICAgICAgLy8gU2VtZWxoYW50ZSBhIGNvbW8KICAgICAgICAvLyAxIHJlYWwgPSAxMDAgY2VudGF2b3MKICAgICAgICAvLyAxIHRva2VuID0gMSAqICgxMCAqKiBkZWNpbWFscykKICAgICAgICBfbWludChtc2cuc2VuZGVyLCAxMDAgKiAxMCoqdWludChkZWNpbWFscygpKSk7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) +- [TokenSwap.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmltcG9ydCAiaHR0cHM6Ly9naXRodWIuY29tL09wZW5aZXBwZWxpbi9vcGVuemVwcGVsaW4tY29udHJhY3RzL2Jsb2IvdjQuMC4wL2NvbnRyYWN0cy90b2tlbi9FUkMyMC9JRVJDMjAuc29sIjsKCi8qCkNvbW8gdHJvY2FyIHRva2VucwoKMS4gQWxpY2UgdGVtIDEwMCB0b2tlbnMgZGUgQWxpY2VDb2luLCBxdWUgZSB1bSB0b2tlbiBFUkMyMC4KMi4gQm9iIHRlbSAxMDAgdG9rZW5zIGRlIEJvYkNvaW4sIHF1ZSB0YW1iZW0gZSB1bSB0b2tlbiBFUkMyMC4KMy4gQWxpY2UgZSBCb2IgcXVlcmVtIG5lZ29jaWFyIDEwIEFsaWNlQ29pbiBwb3IgMjAgQm9iQ29pbi4KNC4gQWxpY2Ugb3UgQm9iIGltcGxhbnRhbSBUb2tlblN3YXAKNS4gQWxpY2UgYXByb3ZhIFRva2VuU3dhcCBwYXJhIHJldGlyYXIgMTAgdG9rZW5zIGRlIEFsaWNlQ29pbgo2LiBCb2IgYXByb3ZhIFRva2VuU3dhcCBwYXJhIHJldGlyYXIgMjAgdG9rZW5zIGRlIEJvYkNvaW4KNy4gQWxpY2Ugb3UgQm9iIGNoYW1hbSBUb2tlblN3YXAuc3dhcCgpCjguIEFsaWNlIGUgQm9iIG5lZ29jaWFyYW0gdG9rZW5zIGNvbSBzdWNlc3NvLgoqLwoKY29udHJhY3QgVG9rZW5Td2FwIHsKICAgIElFUkMyMCBwdWJsaWMgdG9rZW4xOwogICAgYWRkcmVzcyBwdWJsaWMgb3duZXIxOwogICAgdWludCBwdWJsaWMgYW1vdW50MTsKICAgIElFUkMyMCBwdWJsaWMgdG9rZW4yOwogICAgYWRkcmVzcyBwdWJsaWMgb3duZXIyOwogICAgdWludCBwdWJsaWMgYW1vdW50MjsKCiAgICBjb25zdHJ1Y3RvcigKICAgICAgICBhZGRyZXNzIF90b2tlbjEsCiAgICAgICAgYWRkcmVzcyBfb3duZXIxLAogICAgICAgIHVpbnQgX2Ftb3VudDEsCiAgICAgICAgYWRkcmVzcyBfdG9rZW4yLAogICAgICAgIGFkZHJlc3MgX293bmVyMiwKICAgICAgICB1aW50IF9hbW91bnQyCiAgICApIHsKICAgICAgICB0b2tlbjEgPSBJRVJDMjAoX3Rva2VuMSk7CiAgICAgICAgb3duZXIxID0gX293bmVyMTsKICAgICAgICBhbW91bnQxID0gX2Ftb3VudDE7CiAgICAgICAgdG9rZW4yID0gSUVSQzIwKF90b2tlbjIpOwogICAgICAgIG93bmVyMiA9IF9vd25lcjI7CiAgICAgICAgYW1vdW50MiA9IF9hbW91bnQyOwogICAgfQoKICAgIGZ1bmN0aW9uIHN3YXAoKSBwdWJsaWMgewogICAgICAgIHJlcXVpcmUobXNnLnNlbmRlciA9PSBvd25lcjEgfHwgbXNnLnNlbmRlciA9PSBvd25lcjIsICJOb3QgYXV0aG9yaXplZCIpOwogICAgICAgIHJlcXVpcmUoCiAgICAgICAgICAgIHRva2VuMS5hbGxvd2FuY2Uob3duZXIxLCBhZGRyZXNzKHRoaXMpKSA+PSBhbW91bnQxLAogICAgICAgICAgICAiVG9rZW4gMSBhbGxvd2FuY2UgdG9vIGxvdyIKICAgICAgICApOwogICAgICAgIHJlcXVpcmUoCiAgICAgICAgICAgIHRva2VuMi5hbGxvd2FuY2Uob3duZXIyLCBhZGRyZXNzKHRoaXMpKSA+PSBhbW91bnQyLAogICAgICAgICAgICAiVG9rZW4gMiBhbGxvd2FuY2UgdG9vIGxvdyIKICAgICAgICApOwoKICAgICAgICBfc2FmZVRyYW5zZmVyRnJvbSh0b2tlbjEsIG93bmVyMSwgb3duZXIyLCBhbW91bnQxKTsKICAgICAgICBfc2FmZVRyYW5zZmVyRnJvbSh0b2tlbjIsIG93bmVyMiwgb3duZXIxLCBhbW91bnQyKTsKICAgIH0KCiAgICBmdW5jdGlvbiBfc2FmZVRyYW5zZmVyRnJvbSgKICAgICAgICBJRVJDMjAgdG9rZW4sCiAgICAgICAgYWRkcmVzcyBzZW5kZXIsCiAgICAgICAgYWRkcmVzcyByZWNpcGllbnQsCiAgICAgICAgdWludCBhbW91bnQKICAgICkgcHJpdmF0ZSB7CiAgICAgICAgYm9vbCBzZW50ID0gdG9rZW4udHJhbnNmZXJGcm9tKHNlbmRlciwgcmVjaXBpZW50LCBhbW91bnQpOwogICAgICAgIHJlcXVpcmUoc2VudCwgIlRva2VuIHRyYW5zZmVyIGZhaWxlZCIpOwogICAgfQp9&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/erc721.md b/src/exemplos/aplicacoes/erc721.md new file mode 100644 index 0000000..2009792 --- /dev/null +++ b/src/exemplos/aplicacoes/erc721.md @@ -0,0 +1,214 @@ +# ERC721 + +Exemplo de um contrato ERC721. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IERC165 { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +interface IERC721 is IERC165 { + function balanceOf(address owner) external view returns (uint balance); + + function ownerOf(uint tokenId) external view returns (address owner); + + function safeTransferFrom( + address from, + address to, + uint tokenId + ) external; + + function safeTransferFrom( + address from, + address to, + uint tokenId, + bytes calldata data + ) external; + + function transferFrom( + address from, + address to, + uint tokenId + ) external; + + function approve(address to, uint tokenId) external; + + function getApproved(uint tokenId) external view returns (address operator); + + function setApprovalForAll(address operator, bool _approved) external; + + function isApprovedForAll(address owner, address operator) + external + view + returns (bool); +} + +interface IERC721Receiver { + function onERC721Received( + address operator, + address from, + uint tokenId, + bytes calldata data + ) external returns (bytes4); +} + +contract ERC721 is IERC721 { + event Transfer(address indexed from, address indexed to, uint indexed id); + event Approval(address indexed owner, address indexed spender, uint indexed id); + event ApprovalForAll( + address indexed owner, + address indexed operator, + bool approved + ); + + // Mapping de token ID para endereço do dono + mapping(uint => address) internal _ownerOf; + + // Mapping do endereço do dono para contagem de tokens + mapping(address => uint) internal _balanceOf; + + // Mapping de token ID para endereços aprovados + mapping(uint => address) internal _approvals; + + // Mapping do dono para operatores aprovados + mapping(address => mapping(address => bool)) public isApprovedForAll; + + function supportsInterface(bytes4 interfaceId) external pure returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + + function ownerOf(uint id) external view returns (address owner) { + owner = _ownerOf[id]; + require(owner != address(0), "token doesn't exist"); + } + + function balanceOf(address owner) external view returns (uint) { + require(owner != address(0), "owner = zero address"); + return _balanceOf[owner]; + } + + function setApprovalForAll(address operator, bool approved) external { + isApprovedForAll[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + + function approve(address spender, uint id) external { + address owner = _ownerOf[id]; + require( + msg.sender == owner || isApprovedForAll[owner][msg.sender], + "not authorized" + ); + + _approvals[id] = spender; + + emit Approval(owner, spender, id); + } + + function getApproved(uint id) external view returns (address) { + require(_ownerOf[id] != address(0), "token doesn't exist"); + return _approvals[id]; + } + + function _isApprovedOrOwner( + address owner, + address spender, + uint id + ) internal view returns (bool) { + return (spender == owner || + isApprovedForAll[owner][spender] || + spender == _approvals[id]); + } + + function transferFrom( + address from, + address to, + uint id + ) public { + require(from == _ownerOf[id], "from != owner"); + require(to != address(0), "transfer to zero address"); + + require(_isApprovedOrOwner(from, msg.sender, id), "not authorized"); + + _balanceOf[from]--; + _balanceOf[to]++; + _ownerOf[id] = to; + + delete _approvals[id]; + + emit Transfer(from, to, id); + } + + function safeTransferFrom( + address from, + address to, + uint id + ) external { + transferFrom(from, to, id); + + require( + to.code.length == 0 || + IERC721Receiver(to).onERC721Received(msg.sender, from, id, "") == + IERC721Receiver.onERC721Received.selector, + "unsafe recipient" + ); + } + + function safeTransferFrom( + address from, + address to, + uint id, + bytes calldata data + ) external { + transferFrom(from, to, id); + + require( + to.code.length == 0 || + IERC721Receiver(to).onERC721Received(msg.sender, from, id, data) == + IERC721Receiver.onERC721Received.selector, + "unsafe recipient" + ); + } + + function _mint(address to, uint id) internal { + require(to != address(0), "mint to zero address"); + require(_ownerOf[id] == address(0), "already minted"); + + _balanceOf[to]++; + _ownerOf[id] = to; + + emit Transfer(address(0), to, id); + } + + function _burn(uint id) internal { + address owner = _ownerOf[id]; + require(owner != address(0), "not minted"); + + _balanceOf[owner] -= 1; + + delete _ownerOf[id]; + delete _approvals[id]; + + emit Transfer(owner, address(0), id); + } +} + +contract MyNFT is ERC721 { + function mint(address to, uint id) external { + _mint(to, id); + } + + function burn(uint id) external { + require(msg.sender == _ownerOf[id], "not owner"); + _burn(id); + } +} +``` + +## Teste no Remix + +- [ERC721.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmludGVyZmFjZSBJRVJDMTY1IHsKICAgIGZ1bmN0aW9uIHN1cHBvcnRzSW50ZXJmYWNlKGJ5dGVzNCBpbnRlcmZhY2VJRCkgZXh0ZXJuYWwgdmlldyByZXR1cm5zIChib29sKTsKfQoKaW50ZXJmYWNlIElFUkM3MjEgaXMgSUVSQzE2NSB7CiAgICBmdW5jdGlvbiBiYWxhbmNlT2YoYWRkcmVzcyBvd25lcikgZXh0ZXJuYWwgdmlldyByZXR1cm5zICh1aW50IGJhbGFuY2UpOwoKICAgIGZ1bmN0aW9uIG93bmVyT2YodWludCB0b2tlbklkKSBleHRlcm5hbCB2aWV3IHJldHVybnMgKGFkZHJlc3Mgb3duZXIpOwoKICAgIGZ1bmN0aW9uIHNhZmVUcmFuc2ZlckZyb20oCiAgICAgICAgYWRkcmVzcyBmcm9tLAogICAgICAgIGFkZHJlc3MgdG8sCiAgICAgICAgdWludCB0b2tlbklkCiAgICApIGV4dGVybmFsOwoKICAgIGZ1bmN0aW9uIHNhZmVUcmFuc2ZlckZyb20oCiAgICAgICAgYWRkcmVzcyBmcm9tLAogICAgICAgIGFkZHJlc3MgdG8sCiAgICAgICAgdWludCB0b2tlbklkLAogICAgICAgIGJ5dGVzIGNhbGxkYXRhIGRhdGEKICAgICkgZXh0ZXJuYWw7CgogICAgZnVuY3Rpb24gdHJhbnNmZXJGcm9tKAogICAgICAgIGFkZHJlc3MgZnJvbSwKICAgICAgICBhZGRyZXNzIHRvLAogICAgICAgIHVpbnQgdG9rZW5JZAogICAgKSBleHRlcm5hbDsKCiAgICBmdW5jdGlvbiBhcHByb3ZlKGFkZHJlc3MgdG8sIHVpbnQgdG9rZW5JZCkgZXh0ZXJuYWw7CgogICAgZnVuY3Rpb24gZ2V0QXBwcm92ZWQodWludCB0b2tlbklkKSBleHRlcm5hbCB2aWV3IHJldHVybnMgKGFkZHJlc3Mgb3BlcmF0b3IpOwoKICAgIGZ1bmN0aW9uIHNldEFwcHJvdmFsRm9yQWxsKGFkZHJlc3Mgb3BlcmF0b3IsIGJvb2wgX2FwcHJvdmVkKSBleHRlcm5hbDsKCiAgICBmdW5jdGlvbiBpc0FwcHJvdmVkRm9yQWxsKGFkZHJlc3Mgb3duZXIsIGFkZHJlc3Mgb3BlcmF0b3IpCiAgICAgICAgZXh0ZXJuYWwKICAgICAgICB2aWV3CiAgICAgICAgcmV0dXJucyAoYm9vbCk7Cn0KCmludGVyZmFjZSBJRVJDNzIxUmVjZWl2ZXIgewogICAgZnVuY3Rpb24gb25FUkM3MjFSZWNlaXZlZCgKICAgICAgICBhZGRyZXNzIG9wZXJhdG9yLAogICAgICAgIGFkZHJlc3MgZnJvbSwKICAgICAgICB1aW50IHRva2VuSWQsCiAgICAgICAgYnl0ZXMgY2FsbGRhdGEgZGF0YQogICAgKSBleHRlcm5hbCByZXR1cm5zIChieXRlczQpOwp9Cgpjb250cmFjdCBFUkM3MjEgaXMgSUVSQzcyMSB7CiAgICBldmVudCBUcmFuc2ZlcihhZGRyZXNzIGluZGV4ZWQgZnJvbSwgYWRkcmVzcyBpbmRleGVkIHRvLCB1aW50IGluZGV4ZWQgaWQpOwogICAgZXZlbnQgQXBwcm92YWwoYWRkcmVzcyBpbmRleGVkIG93bmVyLCBhZGRyZXNzIGluZGV4ZWQgc3BlbmRlciwgdWludCBpbmRleGVkIGlkKTsKICAgIGV2ZW50IEFwcHJvdmFsRm9yQWxsKAogICAgICAgIGFkZHJlc3MgaW5kZXhlZCBvd25lciwKICAgICAgICBhZGRyZXNzIGluZGV4ZWQgb3BlcmF0b3IsCiAgICAgICAgYm9vbCBhcHByb3ZlZAogICAgKTsKCiAgICAvLyBNYXBwaW5nIGRlIHRva2VuIElEIHBhcmEgZW5kZXJlY28gZG8gZG9ubwogICAgbWFwcGluZyh1aW50ID0+IGFkZHJlc3MpIGludGVybmFsIF9vd25lck9mOwoKICAgIC8vIE1hcHBpbmcgZG8gZW5kZXJlY28gZG8gZG9ubyBwYXJhIGNvbnRhZ2VtIGRlIHRva2VucwogICAgbWFwcGluZyhhZGRyZXNzID0+IHVpbnQpIGludGVybmFsIF9iYWxhbmNlT2Y7CgogICAgLy8gTWFwcGluZyBkZSB0b2tlbiBJRCBwYXJhIGVuZGVyZWNvcyBhcHJvdmFkb3MKICAgIG1hcHBpbmcodWludCA9PiBhZGRyZXNzKSBpbnRlcm5hbCBfYXBwcm92YWxzOwoKICAgIC8vIE1hcHBpbmcgZG8gZG9ubyBwYXJhIG9wZXJhdG9yZXMgYXByb3ZhZG9zCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gbWFwcGluZyhhZGRyZXNzID0+IGJvb2wpKSBwdWJsaWMgaXNBcHByb3ZlZEZvckFsbDsKCiAgICBmdW5jdGlvbiBzdXBwb3J0c0ludGVyZmFjZShieXRlczQgaW50ZXJmYWNlSWQpIGV4dGVybmFsIHB1cmUgcmV0dXJucyAoYm9vbCkgewogICAgICAgIHJldHVybgogICAgICAgICAgICBpbnRlcmZhY2VJZCA9PSB0eXBlKElFUkM3MjEpLmludGVyZmFjZUlkIHx8CiAgICAgICAgICAgIGludGVyZmFjZUlkID09IHR5cGUoSUVSQzE2NSkuaW50ZXJmYWNlSWQ7CiAgICB9CgogICAgZnVuY3Rpb24gb3duZXJPZih1aW50IGlkKSBleHRlcm5hbCB2aWV3IHJldHVybnMgKGFkZHJlc3Mgb3duZXIpIHsKICAgICAgICBvd25lciA9IF9vd25lck9mW2lkXTsKICAgICAgICByZXF1aXJlKG93bmVyICE9IGFkZHJlc3MoMCksICJ0b2tlbiBkb2Vzbid0IGV4aXN0Iik7CiAgICB9CgogICAgZnVuY3Rpb24gYmFsYW5jZU9mKGFkZHJlc3Mgb3duZXIpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAodWludCkgewogICAgICAgIHJlcXVpcmUob3duZXIgIT0gYWRkcmVzcygwKSwgIm93bmVyID0gemVybyBhZGRyZXNzIik7CiAgICAgICAgcmV0dXJuIF9iYWxhbmNlT2Zbb3duZXJdOwogICAgfQoKICAgIGZ1bmN0aW9uIHNldEFwcHJvdmFsRm9yQWxsKGFkZHJlc3Mgb3BlcmF0b3IsIGJvb2wgYXBwcm92ZWQpIGV4dGVybmFsIHsKICAgICAgICBpc0FwcHJvdmVkRm9yQWxsW21zZy5zZW5kZXJdW29wZXJhdG9yXSA9IGFwcHJvdmVkOwogICAgICAgIGVtaXQgQXBwcm92YWxGb3JBbGwobXNnLnNlbmRlciwgb3BlcmF0b3IsIGFwcHJvdmVkKTsKICAgIH0KCiAgICBmdW5jdGlvbiBhcHByb3ZlKGFkZHJlc3Mgc3BlbmRlciwgdWludCBpZCkgZXh0ZXJuYWwgewogICAgICAgIGFkZHJlc3Mgb3duZXIgPSBfb3duZXJPZltpZF07CiAgICAgICAgcmVxdWlyZSgKICAgICAgICAgICAgbXNnLnNlbmRlciA9PSBvd25lciB8fCBpc0FwcHJvdmVkRm9yQWxsW293bmVyXVttc2cuc2VuZGVyXSwKICAgICAgICAgICAgIm5vdCBhdXRob3JpemVkIgogICAgICAgICk7CgogICAgICAgIF9hcHByb3ZhbHNbaWRdID0gc3BlbmRlcjsKCiAgICAgICAgZW1pdCBBcHByb3ZhbChvd25lciwgc3BlbmRlciwgaWQpOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldEFwcHJvdmVkKHVpbnQgaWQpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAoYWRkcmVzcykgewogICAgICAgIHJlcXVpcmUoX293bmVyT2ZbaWRdICE9IGFkZHJlc3MoMCksICJ0b2tlbiBkb2Vzbid0IGV4aXN0Iik7CiAgICAgICAgcmV0dXJuIF9hcHByb3ZhbHNbaWRdOwogICAgfQoKICAgIGZ1bmN0aW9uIF9pc0FwcHJvdmVkT3JPd25lcigKICAgICAgICBhZGRyZXNzIG93bmVyLAogICAgICAgIGFkZHJlc3Mgc3BlbmRlciwKICAgICAgICB1aW50IGlkCiAgICApIGludGVybmFsIHZpZXcgcmV0dXJucyAoYm9vbCkgewogICAgICAgIHJldHVybiAoc3BlbmRlciA9PSBvd25lciB8fAogICAgICAgICAgICBpc0FwcHJvdmVkRm9yQWxsW293bmVyXVtzcGVuZGVyXSB8fAogICAgICAgICAgICBzcGVuZGVyID09IF9hcHByb3ZhbHNbaWRdKTsKICAgIH0KCiAgICBmdW5jdGlvbiB0cmFuc2ZlckZyb20oCiAgICAgICAgYWRkcmVzcyBmcm9tLAogICAgICAgIGFkZHJlc3MgdG8sCiAgICAgICAgdWludCBpZAogICAgKSBwdWJsaWMgewogICAgICAgIHJlcXVpcmUoZnJvbSA9PSBfb3duZXJPZltpZF0sICJmcm9tICE9IG93bmVyIik7CiAgICAgICAgcmVxdWlyZSh0byAhPSBhZGRyZXNzKDApLCAidHJhbnNmZXIgdG8gemVybyBhZGRyZXNzIik7CgogICAgICAgIHJlcXVpcmUoX2lzQXBwcm92ZWRPck93bmVyKGZyb20sIG1zZy5zZW5kZXIsIGlkKSwgIm5vdCBhdXRob3JpemVkIik7CgogICAgICAgIF9iYWxhbmNlT2ZbZnJvbV0tLTsKICAgICAgICBfYmFsYW5jZU9mW3RvXSsrOwogICAgICAgIF9vd25lck9mW2lkXSA9IHRvOwoKICAgICAgICBkZWxldGUgX2FwcHJvdmFsc1tpZF07CgogICAgICAgIGVtaXQgVHJhbnNmZXIoZnJvbSwgdG8sIGlkKTsKICAgIH0KCiAgICBmdW5jdGlvbiBzYWZlVHJhbnNmZXJGcm9tKAogICAgICAgIGFkZHJlc3MgZnJvbSwKICAgICAgICBhZGRyZXNzIHRvLAogICAgICAgIHVpbnQgaWQKICAgICkgZXh0ZXJuYWwgewogICAgICAgIHRyYW5zZmVyRnJvbShmcm9tLCB0bywgaWQpOwoKICAgICAgICByZXF1aXJlKAogICAgICAgICAgICB0by5jb2RlLmxlbmd0aCA9PSAwIHx8CiAgICAgICAgICAgICAgICBJRVJDNzIxUmVjZWl2ZXIodG8pLm9uRVJDNzIxUmVjZWl2ZWQobXNnLnNlbmRlciwgZnJvbSwgaWQsICIiKSA9PQogICAgICAgICAgICAgICAgSUVSQzcyMVJlY2VpdmVyLm9uRVJDNzIxUmVjZWl2ZWQuc2VsZWN0b3IsCiAgICAgICAgICAgICJ1bnNhZmUgcmVjaXBpZW50IgogICAgICAgICk7CiAgICB9CgogICAgZnVuY3Rpb24gc2FmZVRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzIGZyb20sCiAgICAgICAgYWRkcmVzcyB0bywKICAgICAgICB1aW50IGlkLAogICAgICAgIGJ5dGVzIGNhbGxkYXRhIGRhdGEKICAgICkgZXh0ZXJuYWwgewogICAgICAgIHRyYW5zZmVyRnJvbShmcm9tLCB0bywgaWQpOwoKICAgICAgICByZXF1aXJlKAogICAgICAgICAgICB0by5jb2RlLmxlbmd0aCA9PSAwIHx8CiAgICAgICAgICAgICAgICBJRVJDNzIxUmVjZWl2ZXIodG8pLm9uRVJDNzIxUmVjZWl2ZWQobXNnLnNlbmRlciwgZnJvbSwgaWQsIGRhdGEpID09CiAgICAgICAgICAgICAgICBJRVJDNzIxUmVjZWl2ZXIub25FUkM3MjFSZWNlaXZlZC5zZWxlY3RvciwKICAgICAgICAgICAgInVuc2FmZSByZWNpcGllbnQiCiAgICAgICAgKTsKICAgIH0KCiAgICBmdW5jdGlvbiBfbWludChhZGRyZXNzIHRvLCB1aW50IGlkKSBpbnRlcm5hbCB7CiAgICAgICAgcmVxdWlyZSh0byAhPSBhZGRyZXNzKDApLCAibWludCB0byB6ZXJvIGFkZHJlc3MiKTsKICAgICAgICByZXF1aXJlKF9vd25lck9mW2lkXSA9PSBhZGRyZXNzKDApLCAiYWxyZWFkeSBtaW50ZWQiKTsKCiAgICAgICAgX2JhbGFuY2VPZlt0b10rKzsKICAgICAgICBfb3duZXJPZltpZF0gPSB0bzsKCiAgICAgICAgZW1pdCBUcmFuc2ZlcihhZGRyZXNzKDApLCB0bywgaWQpOwogICAgfQoKICAgIGZ1bmN0aW9uIF9idXJuKHVpbnQgaWQpIGludGVybmFsIHsKICAgICAgICBhZGRyZXNzIG93bmVyID0gX293bmVyT2ZbaWRdOwogICAgICAgIHJlcXVpcmUob3duZXIgIT0gYWRkcmVzcygwKSwgIm5vdCBtaW50ZWQiKTsKCiAgICAgICAgX2JhbGFuY2VPZltvd25lcl0gLT0gMTsKCiAgICAgICAgZGVsZXRlIF9vd25lck9mW2lkXTsKICAgICAgICBkZWxldGUgX2FwcHJvdmFsc1tpZF07CgogICAgICAgIGVtaXQgVHJhbnNmZXIob3duZXIsIGFkZHJlc3MoMCksIGlkKTsKICAgIH0KfQoKY29udHJhY3QgTXlORlQgaXMgRVJDNzIxIHsKICAgIGZ1bmN0aW9uIG1pbnQoYWRkcmVzcyB0bywgdWludCBpZCkgZXh0ZXJuYWwgewogICAgICAgIF9taW50KHRvLCBpZCk7CiAgICB9CgogICAgZnVuY3Rpb24gYnVybih1aW50IGlkKSBleHRlcm5hbCB7CiAgICAgICAgcmVxdWlyZShtc2cuc2VuZGVyID09IF9vd25lck9mW2lkXSwgIm5vdCBvd25lciIpOwogICAgICAgIF9idXJuKGlkKTsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/exponenciacao-binaria-em-assembly.md b/src/exemplos/aplicacoes/exponenciacao-binaria-em-assembly.md new file mode 100644 index 0000000..30a3a2a --- /dev/null +++ b/src/exemplos/aplicacoes/exponenciacao-binaria-em-assembly.md @@ -0,0 +1,66 @@ +# Exponenciação binária em assembly + +Exemplo de exponenciação binária em assembly + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract AssemblyBinExp { + // Binary exponentiation to calculate x**n + function rpow(uint256 x, uint256 n, uint256 b) + public + pure + returns (uint256 z) + { + assembly { + switch x + // x = 0 + case 0 { + switch n + // n = 0 --> x**n = 0**0 --> 1 + case 0 { z := b } + // n > 0 --> x**n = 0**n --> 0 + default { z := 0 } + } + default { + switch mod(n, 2) + // x > 0 and n is even --> z = 1 + case 0 { z := b } + // x > 0 and n is odd --> z = x + default { z := x } + + let half := div(b, 2) // for rounding. + // n = n / 2, while n > 0, n = n / 2 + for { n := div(n, 2) } n { n := div(n, 2) } { + let xx := mul(x, x) + // Check overflow - revert if xx / x != x + if iszero(eq(div(xx, x), x)) { revert(0, 0) } + // Round (xx + half) / b + let xxRound := add(xx, half) + // Check overflow - revert if xxRound < xx + if lt(xxRound, xx) { revert(0, 0) } + x := div(xxRound, b) + // if n % 2 == 1 + if mod(n, 2) { + let zx := mul(z, x) + // revert if x != 0 and zx / x != z + if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { + revert(0, 0) + } + // Round (zx + half) / b + let zxRound := add(zx, half) + // Check overflow - revert if zxRound < zx + if lt(zxRound, zx) { revert(0, 0) } + z := div(zxRound, b) + } + } + } + } + } +} +``` + +## Teste no Remix + +- [EtherWallet.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IEFzc2VtYmx5QmluRXhwIHsKICAgIC8vIEJpbmFyeSBleHBvbmVudGlhdGlvbiB0byBjYWxjdWxhdGUgeCoqbgogICAgZnVuY3Rpb24gcnBvdyh1aW50MjU2IHgsIHVpbnQyNTYgbiwgdWludDI1NiBiKQogICAgICAgIHB1YmxpYwogICAgICAgIHB1cmUKICAgICAgICByZXR1cm5zICh1aW50MjU2IHopCiAgICB7CiAgICAgICAgYXNzZW1ibHkgewogICAgICAgICAgICBzd2l0Y2ggeAogICAgICAgICAgICAvLyB4ID0gMAogICAgICAgICAgICBjYXNlIDAgewogICAgICAgICAgICAgICAgc3dpdGNoIG4KICAgICAgICAgICAgICAgIC8vIG4gPSAwIC0tPiB4KipuID0gMCoqMCAtLT4gMQogICAgICAgICAgICAgICAgY2FzZSAwIHsgeiA6PSBiIH0KICAgICAgICAgICAgICAgIC8vIG4gPiAwIC0tPiB4KipuID0gMCoqbiAtLT4gMAogICAgICAgICAgICAgICAgZGVmYXVsdCB7IHogOj0gMCB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZGVmYXVsdCB7CiAgICAgICAgICAgICAgICBzd2l0Y2ggbW9kKG4sIDIpCiAgICAgICAgICAgICAgICAvLyB4ID4gMCBhbmQgbiBpcyBldmVuIC0tPiB6ID0gMQogICAgICAgICAgICAgICAgY2FzZSAwIHsgeiA6PSBiIH0KICAgICAgICAgICAgICAgIC8vIHggPiAwIGFuZCBuIGlzIG9kZCAtLT4geiA9IHgKICAgICAgICAgICAgICAgIGRlZmF1bHQgeyB6IDo9IHggfQoKICAgICAgICAgICAgICAgIGxldCBoYWxmIDo9IGRpdihiLCAyKSAvLyBmb3Igcm91bmRpbmcuCiAgICAgICAgICAgICAgICAvLyBuID0gbiAvIDIsIHdoaWxlIG4gPiAwLCBuID0gbiAvIDIKICAgICAgICAgICAgICAgIGZvciB7IG4gOj0gZGl2KG4sIDIpIH0gbiB7IG4gOj0gZGl2KG4sIDIpIH0gewogICAgICAgICAgICAgICAgICAgIGxldCB4eCA6PSBtdWwoeCwgeCkKICAgICAgICAgICAgICAgICAgICAvLyBDaGVjayBvdmVyZmxvdyAtIHJldmVydCBpZiB4eCAvIHggIT0geAogICAgICAgICAgICAgICAgICAgIGlmIGlzemVybyhlcShkaXYoeHgsIHgpLCB4KSkgeyByZXZlcnQoMCwgMCkgfQogICAgICAgICAgICAgICAgICAgIC8vIFJvdW5kICh4eCArIGhhbGYpIC8gYgogICAgICAgICAgICAgICAgICAgIGxldCB4eFJvdW5kIDo9IGFkZCh4eCwgaGFsZikKICAgICAgICAgICAgICAgICAgICAvLyBDaGVjayBvdmVyZmxvdyAtIHJldmVydCBpZiB4eFJvdW5kIDwgeHgKICAgICAgICAgICAgICAgICAgICBpZiBsdCh4eFJvdW5kLCB4eCkgeyByZXZlcnQoMCwgMCkgfQogICAgICAgICAgICAgICAgICAgIHggOj0gZGl2KHh4Um91bmQsIGIpCiAgICAgICAgICAgICAgICAgICAgLy8gaWYgbiAlIDIgPT0gMQogICAgICAgICAgICAgICAgICAgIGlmIG1vZChuLCAyKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGxldCB6eCA6PSBtdWwoeiwgeCkKICAgICAgICAgICAgICAgICAgICAgICAgLy8gcmV2ZXJ0IGlmIHggIT0gMCBhbmQgenggLyB4ICE9IHoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgYW5kKGlzemVybyhpc3plcm8oeCkpLCBpc3plcm8oZXEoZGl2KHp4LCB4KSwgeikpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXZlcnQoMCwgMCkKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAvLyBSb3VuZCAoenggKyBoYWxmKSAvIGIKICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHp4Um91bmQgOj0gYWRkKHp4LCBoYWxmKQogICAgICAgICAgICAgICAgICAgICAgICAvLyBDaGVjayBvdmVyZmxvdyAtIHJldmVydCBpZiB6eFJvdW5kIDwgengKICAgICAgICAgICAgICAgICAgICAgICAgaWYgbHQoenhSb3VuZCwgengpIHsgcmV2ZXJ0KDAsIDApIH0KICAgICAgICAgICAgICAgICAgICAgICAgeiA6PSBkaXYoenhSb3VuZCwgYikKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9Cn0K=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/gravar-em-qualquer-slot.md b/src/exemplos/aplicacoes/gravar-em-qualquer-slot.md new file mode 100644 index 0000000..983499f --- /dev/null +++ b/src/exemplos/aplicacoes/gravar-em-qualquer-slot.md @@ -0,0 +1,46 @@ +# Gravar em qualquer slot + +O armazenamento de solidity é como um array de comprimento 2256. Cada slot no array pode armazenar 32 bytes. + +As variáveis ​​de estado definem quais slots serão usados ​​para armazenar dados. + +No entanto, usando assembly, você pode gravar em qualquer slot. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +library StorageSlot { + // Envolver o endereço numa estrutura para que possa ser passado como um ponteiro de armazenamento + struct AddressSlot { + address value; + } + + function getAddressSlot( + bytes32 slot + ) internal pure returns (AddressSlot storage pointer) { + assembly { + // Obtém o ponteiro para AddressSlot armazenado na slot + pointer.slot := slot + } + } +} + +contract TestSlot { + bytes32 public constant TEST_SLOT = keccak256("TEST_SLOT"); + + function write(address _addr) external { + StorageSlot.AddressSlot storage data = StorageSlot.getAddressSlot(TEST_SLOT); + data.value = _addr; + } + + function get() external view returns (address) { + StorageSlot.AddressSlot storage data = StorageSlot.getAddressSlot(TEST_SLOT); + return data.value; + } +} +``` + +## Teste no Remix + +- [Slot.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmxpYnJhcnkgU3RvcmFnZVNsb3QgewogICAgLy8gRW52b2x2ZXIgbyBlbmRlcmVjbyBudW1hIGVzdHJ1dHVyYSBwYXJhIHF1ZSBwb3NzYSBzZXIgcGFzc2FkbyBjb21vIHVtIHBvbnRlaXJvIGRlIGFybWF6ZW5hbWVudG8KICAgIHN0cnVjdCBBZGRyZXNzU2xvdCB7CiAgICAgICAgYWRkcmVzcyB2YWx1ZTsKICAgIH0KCiAgICBmdW5jdGlvbiBnZXRBZGRyZXNzU2xvdCgKICAgICAgICBieXRlczMyIHNsb3QKICAgICkgaW50ZXJuYWwgcHVyZSByZXR1cm5zIChBZGRyZXNzU2xvdCBzdG9yYWdlIHBvaW50ZXIpIHsKICAgICAgICBhc3NlbWJseSB7CiAgICAgICAgICAgIC8vIE9idGVtIG8gcG9udGVpcm8gcGFyYSBBZGRyZXNzU2xvdCBhcm1hemVuYWRvIG5hIHNsb3QKICAgICAgICAgICAgcG9pbnRlci5zbG90IDo9IHNsb3QKICAgICAgICB9CiAgICB9Cn0KCmNvbnRyYWN0IFRlc3RTbG90IHsKICAgIGJ5dGVzMzIgcHVibGljIGNvbnN0YW50IFRFU1RfU0xPVCA9IGtlY2NhazI1NigiVEVTVF9TTE9UIik7CgogICAgZnVuY3Rpb24gd3JpdGUoYWRkcmVzcyBfYWRkcikgZXh0ZXJuYWwgewogICAgICAgIFN0b3JhZ2VTbG90LkFkZHJlc3NTbG90IHN0b3JhZ2UgZGF0YSA9IFN0b3JhZ2VTbG90LmdldEFkZHJlc3NTbG90KFRFU1RfU0xPVCk7CiAgICAgICAgZGF0YS52YWx1ZSA9IF9hZGRyOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldCgpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAoYWRkcmVzcykgewogICAgICAgIFN0b3JhZ2VTbG90LkFkZHJlc3NTbG90IHN0b3JhZ2UgZGF0YSA9IFN0b3JhZ2VTbG90LmdldEFkZHJlc3NTbG90KFRFU1RfU0xPVCk7CiAgICAgICAgcmV0dXJuIGRhdGEudmFsdWU7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/implante-qualquer-contrato.md b/src/exemplos/aplicacoes/implante-qualquer-contrato.md index 3dfacbf..c6f0a50 100644 --- a/src/exemplos/aplicacoes/implante-qualquer-contrato.md +++ b/src/exemplos/aplicacoes/implante-qualquer-contrato.md @@ -6,12 +6,12 @@ Por este exemplo, você pode obter bytecodes do contrato chamando `Helper.getByt ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; contract Proxy { event Deploy(address); - fallback() external payable {} + receive() external payable {} function deploy(bytes memory _code) external payable returns (address addr) { assembly { @@ -21,7 +21,7 @@ contract Proxy { // n = tamanho do código addr := create(callvalue(), add(_code, 0x20), mload(_code)) } - // retorna endereço 0 em erro + // retorna address 0 on error require(addr != address(0), "deploy failed"); emit Deploy(addr); @@ -70,3 +70,7 @@ contract Helper { } } ``` + +## Teste no Remix + +- [Proxy.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IFByb3h5IHsKICAgIGV2ZW50IERlcGxveShhZGRyZXNzKTsKCiAgICByZWNlaXZlKCkgZXh0ZXJuYWwgcGF5YWJsZSB7fQoKICAgIGZ1bmN0aW9uIGRlcGxveShieXRlcyBtZW1vcnkgX2NvZGUpIGV4dGVybmFsIHBheWFibGUgcmV0dXJucyAoYWRkcmVzcyBhZGRyKSB7CiAgICAgICAgYXNzZW1ibHkgewogICAgICAgICAgICAvLyBjcmVhdGUodiwgcCwgbikKICAgICAgICAgICAgLy8gdiA9IHF1YW50aWRhZGUgZGUgRVRIIGEgc2VyIGVudmlhZG8KICAgICAgICAgICAgLy8gcCA9IHBvbnRlaXJvIG5hIG1lbW9yaWEgcGFyYSBpbmljaWFyIG8gY29kaWdvCiAgICAgICAgICAgIC8vIG4gPSB0YW1hbmhvIGRvIGNvZGlnbwogICAgICAgICAgICBhZGRyIDo9IGNyZWF0ZShjYWxsdmFsdWUoKSwgYWRkKF9jb2RlLCAweDIwKSwgbWxvYWQoX2NvZGUpKQogICAgICAgIH0KICAgICAgICAvLyByZXRvcm5hIGFkZHJlc3MgMCBvbiBlcnJvcgogICAgICAgIHJlcXVpcmUoYWRkciAhPSBhZGRyZXNzKDApLCAiZGVwbG95IGZhaWxlZCIpOwoKICAgICAgICBlbWl0IERlcGxveShhZGRyKTsKICAgIH0KCiAgICBmdW5jdGlvbiBleGVjdXRlKGFkZHJlc3MgX3RhcmdldCwgYnl0ZXMgbWVtb3J5IF9kYXRhKSBleHRlcm5hbCBwYXlhYmxlIHsKICAgICAgICAoYm9vbCBzdWNjZXNzLCApID0gX3RhcmdldC5jYWxse3ZhbHVlOiBtc2cudmFsdWV9KF9kYXRhKTsKICAgICAgICByZXF1aXJlKHN1Y2Nlc3MsICJmYWlsZWQiKTsKICAgIH0KfQoKY29udHJhY3QgVGVzdENvbnRyYWN0MSB7CiAgICBhZGRyZXNzIHB1YmxpYyBvd25lciA9IG1zZy5zZW5kZXI7CgogICAgZnVuY3Rpb24gc2V0T3duZXIoYWRkcmVzcyBfb3duZXIpIHB1YmxpYyB7CiAgICAgICAgcmVxdWlyZShtc2cuc2VuZGVyID09IG93bmVyLCAibm90IG93bmVyIik7CiAgICAgICAgb3duZXIgPSBfb3duZXI7CiAgICB9Cn0KCmNvbnRyYWN0IFRlc3RDb250cmFjdDIgewogICAgYWRkcmVzcyBwdWJsaWMgb3duZXIgPSBtc2cuc2VuZGVyOwogICAgdWludCBwdWJsaWMgdmFsdWUgPSBtc2cudmFsdWU7CiAgICB1aW50IHB1YmxpYyB4OwogICAgdWludCBwdWJsaWMgeTsKCiAgICBjb25zdHJ1Y3Rvcih1aW50IF94LCB1aW50IF95KSBwYXlhYmxlIHsKICAgICAgICB4ID0gX3g7CiAgICAgICAgeSA9IF95OwogICAgfQp9Cgpjb250cmFjdCBIZWxwZXIgewogICAgZnVuY3Rpb24gZ2V0Qnl0ZWNvZGUxKCkgZXh0ZXJuYWwgcHVyZSByZXR1cm5zIChieXRlcyBtZW1vcnkpIHsKICAgICAgICBieXRlcyBtZW1vcnkgYnl0ZWNvZGUgPSB0eXBlKFRlc3RDb250cmFjdDEpLmNyZWF0aW9uQ29kZTsKICAgICAgICByZXR1cm4gYnl0ZWNvZGU7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0Qnl0ZWNvZGUyKHVpbnQgX3gsIHVpbnQgX3kpIGV4dGVybmFsIHB1cmUgcmV0dXJucyAoYnl0ZXMgbWVtb3J5KSB7CiAgICAgICAgYnl0ZXMgbWVtb3J5IGJ5dGVjb2RlID0gdHlwZShUZXN0Q29udHJhY3QyKS5jcmVhdGlvbkNvZGU7CiAgICAgICAgcmV0dXJuIGFiaS5lbmNvZGVQYWNrZWQoYnl0ZWNvZGUsIGFiaS5lbmNvZGUoX3gsIF95KSk7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0Q2FsbGRhdGEoYWRkcmVzcyBfb3duZXIpIGV4dGVybmFsIHB1cmUgcmV0dXJucyAoYnl0ZXMgbWVtb3J5KSB7CiAgICAgICAgcmV0dXJuIGFiaS5lbmNvZGVXaXRoU2lnbmF0dXJlKCJzZXRPd25lcihhZGRyZXNzKSIsIF9vd25lcik7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/leilao-holandes.md b/src/exemplos/aplicacoes/leilao-holandes.md index fabe03b..78f510f 100644 --- a/src/exemplos/aplicacoes/leilao-holandes.md +++ b/src/exemplos/aplicacoes/leilao-holandes.md @@ -7,12 +7,12 @@ Leilão holandês de NFT. 1. O vendedor de NFT implementa este contrato estabelecendo um preço inicial para o NFT. 2. O leilão dura 7 dias. 3. O preço do NFT cai com o tempo -4. Os participantes podem comprar depositando uma quantidade de ETH maior que valor corrente computado pelo contrato inteligente. +4. Os participantes podem comprar depositando uma quantidade de ETH maior que o preço atual calculado pelo contrato inteligente. 5. O leilão termina quando alguém compra o NFT. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; interface IERC721 { function transferFrom( @@ -23,49 +23,57 @@ interface IERC721 { } contract DutchAuction { - event Buy(address winner, uint amount); + uint private constant DURATION = 7 days; IERC721 public immutable nft; uint public immutable nftId; - address payable public seller; - uint public startingPrice; - uint public startAt; - uint public expiresAt; - uint public priceDeductionRate; - address public winner; + address payable public immutable seller; + uint public immutable startingPrice; + uint public immutable startAt; + uint public immutable expiresAt; + uint public immutable discountRate; constructor( uint _startingPrice, - uint _priceDeductionRate, + uint _discountRate, address _nft, uint _nftId ) { seller = payable(msg.sender); startingPrice = _startingPrice; startAt = block.timestamp; - expiresAt = block.timestamp + 7 days; - priceDeductionRate = _priceDeductionRate; + expiresAt = block.timestamp + DURATION; + discountRate = _discountRate; + + require(_startingPrice >= _discountRate * DURATION, "starting price < min"); nft = IERC721(_nft); nftId = _nftId; } + function getPrice() public view returns (uint) { + uint timeElapsed = block.timestamp - startAt; + uint discount = discountRate * timeElapsed; + return startingPrice - discount; + } + function buy() external payable { require(block.timestamp < expiresAt, "auction expired"); - require(winner == address(0), "auction finished"); - - uint timeElapsed = block.timestamp - startAt; - uint deduction = priceDeductionRate * timeElapsed; - uint price = startingPrice - deduction; + uint price = getPrice(); require(msg.value >= price, "ETH < price"); - winner = msg.sender; nft.transferFrom(seller, msg.sender, nftId); - seller.transfer(msg.value); - - emit Buy(msg.sender, msg.value); + uint refund = msg.value - price; + if (refund > 0) { + payable(msg.sender).transfer(refund); + } + selfdestruct(seller); } } ``` + +## Teste no Remix + +- [DutchAuction.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmludGVyZmFjZSBJRVJDNzIxIHsKICAgIGZ1bmN0aW9uIHRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzIF9mcm9tLAogICAgICAgIGFkZHJlc3MgX3RvLAogICAgICAgIHVpbnQgX25mdElkCiAgICApIGV4dGVybmFsOwp9Cgpjb250cmFjdCBEdXRjaEF1Y3Rpb24gewogICAgdWludCBwcml2YXRlIGNvbnN0YW50IERVUkFUSU9OID0gNyBkYXlzOwoKICAgIElFUkM3MjEgcHVibGljIGltbXV0YWJsZSBuZnQ7CiAgICB1aW50IHB1YmxpYyBpbW11dGFibGUgbmZ0SWQ7CgogICAgYWRkcmVzcyBwYXlhYmxlIHB1YmxpYyBpbW11dGFibGUgc2VsbGVyOwogICAgdWludCBwdWJsaWMgaW1tdXRhYmxlIHN0YXJ0aW5nUHJpY2U7CiAgICB1aW50IHB1YmxpYyBpbW11dGFibGUgc3RhcnRBdDsKICAgIHVpbnQgcHVibGljIGltbXV0YWJsZSBleHBpcmVzQXQ7CiAgICB1aW50IHB1YmxpYyBpbW11dGFibGUgZGlzY291bnRSYXRlOwoKICAgIGNvbnN0cnVjdG9yKAogICAgICAgIHVpbnQgX3N0YXJ0aW5nUHJpY2UsCiAgICAgICAgdWludCBfZGlzY291bnRSYXRlLAogICAgICAgIGFkZHJlc3MgX25mdCwKICAgICAgICB1aW50IF9uZnRJZAogICAgKSB7CiAgICAgICAgc2VsbGVyID0gcGF5YWJsZShtc2cuc2VuZGVyKTsKICAgICAgICBzdGFydGluZ1ByaWNlID0gX3N0YXJ0aW5nUHJpY2U7CiAgICAgICAgc3RhcnRBdCA9IGJsb2NrLnRpbWVzdGFtcDsKICAgICAgICBleHBpcmVzQXQgPSBibG9jay50aW1lc3RhbXAgKyBEVVJBVElPTjsKICAgICAgICBkaXNjb3VudFJhdGUgPSBfZGlzY291bnRSYXRlOwoKICAgICAgICByZXF1aXJlKF9zdGFydGluZ1ByaWNlID49IF9kaXNjb3VudFJhdGUgKiBEVVJBVElPTiwgInN0YXJ0aW5nIHByaWNlIDwgbWluIik7CgogICAgICAgIG5mdCA9IElFUkM3MjEoX25mdCk7CiAgICAgICAgbmZ0SWQgPSBfbmZ0SWQ7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0UHJpY2UoKSBwdWJsaWMgdmlldyByZXR1cm5zICh1aW50KSB7CiAgICAgICAgdWludCB0aW1lRWxhcHNlZCA9IGJsb2NrLnRpbWVzdGFtcCAtIHN0YXJ0QXQ7CiAgICAgICAgdWludCBkaXNjb3VudCA9IGRpc2NvdW50UmF0ZSAqIHRpbWVFbGFwc2VkOwogICAgICAgIHJldHVybiBzdGFydGluZ1ByaWNlIC0gZGlzY291bnQ7CiAgICB9CgogICAgZnVuY3Rpb24gYnV5KCkgZXh0ZXJuYWwgcGF5YWJsZSB7CiAgICAgICAgcmVxdWlyZShibG9jay50aW1lc3RhbXAgPCBleHBpcmVzQXQsICJhdWN0aW9uIGV4cGlyZWQiKTsKCiAgICAgICAgdWludCBwcmljZSA9IGdldFByaWNlKCk7CiAgICAgICAgcmVxdWlyZShtc2cudmFsdWUgPj0gcHJpY2UsICJFVEggPCBwcmljZSIpOwoKICAgICAgICBuZnQudHJhbnNmZXJGcm9tKHNlbGxlciwgbXNnLnNlbmRlciwgbmZ0SWQpOwogICAgICAgIHVpbnQgcmVmdW5kID0gbXNnLnZhbHVlIC0gcHJpY2U7CiAgICAgICAgaWYgKHJlZnVuZCA+IDApIHsKICAgICAgICAgICAgcGF5YWJsZShtc2cuc2VuZGVyKS50cmFuc2ZlcihyZWZ1bmQpOwogICAgICAgIH0KICAgICAgICBzZWxmZGVzdHJ1Y3Qoc2VsbGVyKTsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/leilao-ingles.md b/src/exemplos/aplicacoes/leilao-ingles.md index e9b49e3..56c8c8a 100644 --- a/src/exemplos/aplicacoes/leilao-ingles.md +++ b/src/exemplos/aplicacoes/leilao-ingles.md @@ -11,15 +11,19 @@ Leilão inglês de NFT. #### Depois do leilão -1. Quem deu o lance mais alto se torna o possuidor do NFT. +1. Quem deu o lance mais alto se torna o novo proprietário do NFT. 2. O vendedor recebe o lance mais alto de ETH. ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; interface IERC721 { - function transfer(address, uint) external; + function safeTransferFrom( + address from, + address to, + uint tokenId + ) external; function transferFrom( address, @@ -99,13 +103,17 @@ contract EnglishAuction { ended = true; if (highestBidder != address(0)) { - nft.transfer(highestBidder, nftId); + nft.safeTransferFrom(address(this), highestBidder, nftId); seller.transfer(highestBid); } else { - nft.transfer(seller, nftId); + nft.safeTransferFrom(address(this), seller, nftId); } emit End(highestBidder, highestBid); } } ``` + +## Teste no Remix + +- [EnglishAuction.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmludGVyZmFjZSBJRVJDNzIxIHsKICAgIGZ1bmN0aW9uIHNhZmVUcmFuc2ZlckZyb20oCiAgICAgICAgYWRkcmVzcyBmcm9tLAogICAgICAgIGFkZHJlc3MgdG8sCiAgICAgICAgdWludCB0b2tlbklkCiAgICApIGV4dGVybmFsOwoKICAgIGZ1bmN0aW9uIHRyYW5zZmVyRnJvbSgKICAgICAgICBhZGRyZXNzLAogICAgICAgIGFkZHJlc3MsCiAgICAgICAgdWludAogICAgKSBleHRlcm5hbDsKfQoKY29udHJhY3QgRW5nbGlzaEF1Y3Rpb24gewogICAgZXZlbnQgU3RhcnQoKTsKICAgIGV2ZW50IEJpZChhZGRyZXNzIGluZGV4ZWQgc2VuZGVyLCB1aW50IGFtb3VudCk7CiAgICBldmVudCBXaXRoZHJhdyhhZGRyZXNzIGluZGV4ZWQgYmlkZGVyLCB1aW50IGFtb3VudCk7CiAgICBldmVudCBFbmQoYWRkcmVzcyB3aW5uZXIsIHVpbnQgYW1vdW50KTsKCiAgICBJRVJDNzIxIHB1YmxpYyBuZnQ7CiAgICB1aW50IHB1YmxpYyBuZnRJZDsKCiAgICBhZGRyZXNzIHBheWFibGUgcHVibGljIHNlbGxlcjsKICAgIHVpbnQgcHVibGljIGVuZEF0OwogICAgYm9vbCBwdWJsaWMgc3RhcnRlZDsKICAgIGJvb2wgcHVibGljIGVuZGVkOwoKICAgIGFkZHJlc3MgcHVibGljIGhpZ2hlc3RCaWRkZXI7CiAgICB1aW50IHB1YmxpYyBoaWdoZXN0QmlkOwogICAgbWFwcGluZyhhZGRyZXNzID0+IHVpbnQpIHB1YmxpYyBiaWRzOwoKICAgIGNvbnN0cnVjdG9yKAogICAgICAgIGFkZHJlc3MgX25mdCwKICAgICAgICB1aW50IF9uZnRJZCwKICAgICAgICB1aW50IF9zdGFydGluZ0JpZAogICAgKSB7CiAgICAgICAgbmZ0ID0gSUVSQzcyMShfbmZ0KTsKICAgICAgICBuZnRJZCA9IF9uZnRJZDsKCiAgICAgICAgc2VsbGVyID0gcGF5YWJsZShtc2cuc2VuZGVyKTsKICAgICAgICBoaWdoZXN0QmlkID0gX3N0YXJ0aW5nQmlkOwogICAgfQoKICAgIGZ1bmN0aW9uIHN0YXJ0KCkgZXh0ZXJuYWwgewogICAgICAgIHJlcXVpcmUoIXN0YXJ0ZWQsICJzdGFydGVkIik7CiAgICAgICAgcmVxdWlyZShtc2cuc2VuZGVyID09IHNlbGxlciwgIm5vdCBzZWxsZXIiKTsKCiAgICAgICAgbmZ0LnRyYW5zZmVyRnJvbShtc2cuc2VuZGVyLCBhZGRyZXNzKHRoaXMpLCBuZnRJZCk7CiAgICAgICAgc3RhcnRlZCA9IHRydWU7CiAgICAgICAgZW5kQXQgPSBibG9jay50aW1lc3RhbXAgKyA3IGRheXM7CgogICAgICAgIGVtaXQgU3RhcnQoKTsKICAgIH0KCiAgICBmdW5jdGlvbiBiaWQoKSBleHRlcm5hbCBwYXlhYmxlIHsKICAgICAgICByZXF1aXJlKHN0YXJ0ZWQsICJub3Qgc3RhcnRlZCIpOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wIDwgZW5kQXQsICJlbmRlZCIpOwogICAgICAgIHJlcXVpcmUobXNnLnZhbHVlID4gaGlnaGVzdEJpZCwgInZhbHVlIDwgaGlnaGVzdCIpOwoKICAgICAgICBpZiAoaGlnaGVzdEJpZGRlciAhPSBhZGRyZXNzKDApKSB7CiAgICAgICAgICAgIGJpZHNbaGlnaGVzdEJpZGRlcl0gKz0gaGlnaGVzdEJpZDsKICAgICAgICB9CgogICAgICAgIGhpZ2hlc3RCaWRkZXIgPSBtc2cuc2VuZGVyOwogICAgICAgIGhpZ2hlc3RCaWQgPSBtc2cudmFsdWU7CgogICAgICAgIGVtaXQgQmlkKG1zZy5zZW5kZXIsIG1zZy52YWx1ZSk7CiAgICB9CgogICAgZnVuY3Rpb24gd2l0aGRyYXcoKSBleHRlcm5hbCB7CiAgICAgICAgdWludCBiYWwgPSBiaWRzW21zZy5zZW5kZXJdOwogICAgICAgIGJpZHNbbXNnLnNlbmRlcl0gPSAwOwogICAgICAgIHBheWFibGUobXNnLnNlbmRlcikudHJhbnNmZXIoYmFsKTsKCiAgICAgICAgZW1pdCBXaXRoZHJhdyhtc2cuc2VuZGVyLCBiYWwpOwogICAgfQoKICAgIGZ1bmN0aW9uIGVuZCgpIGV4dGVybmFsIHsKICAgICAgICByZXF1aXJlKHN0YXJ0ZWQsICJub3Qgc3RhcnRlZCIpOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wID49IGVuZEF0LCAibm90IGVuZGVkIik7CiAgICAgICAgcmVxdWlyZSghZW5kZWQsICJlbmRlZCIpOwoKICAgICAgICBlbmRlZCA9IHRydWU7CiAgICAgICAgaWYgKGhpZ2hlc3RCaWRkZXIgIT0gYWRkcmVzcygwKSkgewogICAgICAgICAgICBuZnQuc2FmZVRyYW5zZmVyRnJvbShhZGRyZXNzKHRoaXMpLCBoaWdoZXN0QmlkZGVyLCBuZnRJZCk7CiAgICAgICAgICAgIHNlbGxlci50cmFuc2ZlcihoaWdoZXN0QmlkKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBuZnQuc2FmZVRyYW5zZmVyRnJvbShhZGRyZXNzKHRoaXMpLCBzZWxsZXIsIG5mdElkKTsKICAgICAgICB9CgogICAgICAgIGVtaXQgRW5kKGhpZ2hlc3RCaWRkZXIsIGhpZ2hlc3RCaWQpOwogICAgfQp9&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/mapping-iteravel.md b/src/exemplos/aplicacoes/mapping-iteravel.md index 7ce9d80..2fb3f65 100644 --- a/src/exemplos/aplicacoes/mapping-iteravel.md +++ b/src/exemplos/aplicacoes/mapping-iteravel.md @@ -4,10 +4,10 @@ Você não pode iterar através de um `mapping`. Aqui está um exemplo de como c ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +pragma solidity ^0.8.20; library IterableMapping { - // Mapping iterável do endereço ao uint; + // Mapeamento iterável de endereço para uint; struct Map { address[] keys; mapping(address => uint) values; @@ -90,3 +90,7 @@ contract TestIterableMap { } } ``` + +## Teste no Remix + +- [IterableMapping.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmxpYnJhcnkgSXRlcmFibGVNYXBwaW5nIHsKICAgIC8vIE1hcGVhbWVudG8gaXRlcmF2ZWwgZGUgZW5kZXJlY28gcGFyYSB1aW50OwogICAgc3RydWN0IE1hcCB7CiAgICAgICAgYWRkcmVzc1tdIGtleXM7CiAgICAgICAgbWFwcGluZyhhZGRyZXNzID0+IHVpbnQpIHZhbHVlczsKICAgICAgICBtYXBwaW5nKGFkZHJlc3MgPT4gdWludCkgaW5kZXhPZjsKICAgICAgICBtYXBwaW5nKGFkZHJlc3MgPT4gYm9vbCkgaW5zZXJ0ZWQ7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0KE1hcCBzdG9yYWdlIG1hcCwgYWRkcmVzcyBrZXkpIHB1YmxpYyB2aWV3IHJldHVybnMgKHVpbnQpIHsKICAgICAgICByZXR1cm4gbWFwLnZhbHVlc1trZXldOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldEtleUF0SW5kZXgoTWFwIHN0b3JhZ2UgbWFwLCB1aW50IGluZGV4KSBwdWJsaWMgdmlldyByZXR1cm5zIChhZGRyZXNzKSB7CiAgICAgICAgcmV0dXJuIG1hcC5rZXlzW2luZGV4XTsKICAgIH0KCiAgICBmdW5jdGlvbiBzaXplKE1hcCBzdG9yYWdlIG1hcCkgcHVibGljIHZpZXcgcmV0dXJucyAodWludCkgewogICAgICAgIHJldHVybiBtYXAua2V5cy5sZW5ndGg7CiAgICB9CgogICAgZnVuY3Rpb24gc2V0KAogICAgICAgIE1hcCBzdG9yYWdlIG1hcCwKICAgICAgICBhZGRyZXNzIGtleSwKICAgICAgICB1aW50IHZhbAogICAgKSBwdWJsaWMgewogICAgICAgIGlmIChtYXAuaW5zZXJ0ZWRba2V5XSkgewogICAgICAgICAgICBtYXAudmFsdWVzW2tleV0gPSB2YWw7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgbWFwLmluc2VydGVkW2tleV0gPSB0cnVlOwogICAgICAgICAgICBtYXAudmFsdWVzW2tleV0gPSB2YWw7CiAgICAgICAgICAgIG1hcC5pbmRleE9mW2tleV0gPSBtYXAua2V5cy5sZW5ndGg7CiAgICAgICAgICAgIG1hcC5rZXlzLnB1c2goa2V5KTsKICAgICAgICB9CiAgICB9CgogICAgZnVuY3Rpb24gcmVtb3ZlKE1hcCBzdG9yYWdlIG1hcCwgYWRkcmVzcyBrZXkpIHB1YmxpYyB7CiAgICAgICAgaWYgKCFtYXAuaW5zZXJ0ZWRba2V5XSkgewogICAgICAgICAgICByZXR1cm47CiAgICAgICAgfQoKICAgICAgICBkZWxldGUgbWFwLmluc2VydGVkW2tleV07CiAgICAgICAgZGVsZXRlIG1hcC52YWx1ZXNba2V5XTsKCiAgICAgICAgdWludCBpbmRleCA9IG1hcC5pbmRleE9mW2tleV07CiAgICAgICAgdWludCBsYXN0SW5kZXggPSBtYXAua2V5cy5sZW5ndGggLSAxOwogICAgICAgIGFkZHJlc3MgbGFzdEtleSA9IG1hcC5rZXlzW2xhc3RJbmRleF07CgogICAgICAgIG1hcC5pbmRleE9mW2xhc3RLZXldID0gaW5kZXg7CiAgICAgICAgZGVsZXRlIG1hcC5pbmRleE9mW2tleV07CgogICAgICAgIG1hcC5rZXlzW2luZGV4XSA9IGxhc3RLZXk7CiAgICAgICAgbWFwLmtleXMucG9wKCk7CiAgICB9Cn0KCmNvbnRyYWN0IFRlc3RJdGVyYWJsZU1hcCB7CiAgICB1c2luZyBJdGVyYWJsZU1hcHBpbmcgZm9yIEl0ZXJhYmxlTWFwcGluZy5NYXA7CgogICAgSXRlcmFibGVNYXBwaW5nLk1hcCBwcml2YXRlIG1hcDsKCiAgICBmdW5jdGlvbiB0ZXN0SXRlcmFibGVNYXAoKSBwdWJsaWMgewogICAgICAgIG1hcC5zZXQoYWRkcmVzcygwKSwgMCk7CiAgICAgICAgbWFwLnNldChhZGRyZXNzKDEpLCAxMDApOwogICAgICAgIG1hcC5zZXQoYWRkcmVzcygyKSwgMjAwKTsgLy8gaW5zZXJ0CiAgICAgICAgbWFwLnNldChhZGRyZXNzKDIpLCAyMDApOyAvLyB1cGRhdGUKICAgICAgICBtYXAuc2V0KGFkZHJlc3MoMyksIDMwMCk7CgogICAgICAgIGZvciAodWludCBpID0gMDsgaSA8IG1hcC5zaXplKCk7IGkrKykgewogICAgICAgICAgICBhZGRyZXNzIGtleSA9IG1hcC5nZXRLZXlBdEluZGV4KGkpOwoKICAgICAgICAgICAgYXNzZXJ0KG1hcC5nZXQoa2V5KSA9PSBpICogMTAwKTsKICAgICAgICB9CgogICAgICAgIG1hcC5yZW1vdmUoYWRkcmVzcygxKSk7CgogICAgICAgIC8vIGtleXMgPSBbYWRkcmVzcygwKSwgYWRkcmVzcygzKSwgYWRkcmVzcygyKV0KICAgICAgICBhc3NlcnQobWFwLnNpemUoKSA9PSAzKTsKICAgICAgICBhc3NlcnQobWFwLmdldEtleUF0SW5kZXgoMCkgPT0gYWRkcmVzcygwKSk7CiAgICAgICAgYXNzZXJ0KG1hcC5nZXRLZXlBdEluZGV4KDEpID09IGFkZHJlc3MoMykpOwogICAgICAgIGFzc2VydChtYXAuZ2V0S2V5QXRJbmRleCgyKSA9PSBhZGRyZXNzKDIpKTsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) \ No newline at end of file diff --git a/src/exemplos/aplicacoes/multi-chamadas.md b/src/exemplos/aplicacoes/multi-chamadas.md new file mode 100644 index 0000000..dcf3947 --- /dev/null +++ b/src/exemplos/aplicacoes/multi-chamadas.md @@ -0,0 +1,50 @@ +# Multi chamadas + +Um exemplo de contrato que agrega várias consultas usando um loop `for` e `staticcall`. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract MultiCall { + function multiCall(address[] calldata targets, bytes[] calldata data) + external + view + returns (bytes[] memory) + { + require(targets.length == data.length, "target length != data length"); + + bytes[] memory results = new bytes[](data.length); + + for (uint i; i < targets.length; i++) { + (bool success, bytes memory result) = targets[i].staticcall(data[i]); + require(success, "call failed"); + results[i] = result; + } + + return results; + } +} +``` + +Contrato para testar `MultiCall` + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract TestMultiCall { + function test(uint _i) external pure returns (uint) { + return _i; + } + + function getData(uint _i) external pure returns (bytes memory) { + return abi.encodeWithSelector(this.test.selector, _i); + } +} +``` + +## Teste no Remix + +- [MultiCall.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IE11bHRpQ2FsbCB7CiAgICBmdW5jdGlvbiBtdWx0aUNhbGwoYWRkcmVzc1tdIGNhbGxkYXRhIHRhcmdldHMsIGJ5dGVzW10gY2FsbGRhdGEgZGF0YSkKICAgICAgICBleHRlcm5hbAogICAgICAgIHZpZXcKICAgICAgICByZXR1cm5zIChieXRlc1tdIG1lbW9yeSkKICAgIHsKICAgICAgICByZXF1aXJlKHRhcmdldHMubGVuZ3RoID09IGRhdGEubGVuZ3RoLCAidGFyZ2V0IGxlbmd0aCAhPSBkYXRhIGxlbmd0aCIpOwoKICAgICAgICBieXRlc1tdIG1lbW9yeSByZXN1bHRzID0gbmV3IGJ5dGVzW10oZGF0YS5sZW5ndGgpOwoKICAgICAgICBmb3IgKHVpbnQgaTsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgKGJvb2wgc3VjY2VzcywgYnl0ZXMgbWVtb3J5IHJlc3VsdCkgPSB0YXJnZXRzW2ldLnN0YXRpY2NhbGwoZGF0YVtpXSk7CiAgICAgICAgICAgIHJlcXVpcmUoc3VjY2VzcywgImNhbGwgZmFpbGVkIik7CiAgICAgICAgICAgIHJlc3VsdHNbaV0gPSByZXN1bHQ7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gcmVzdWx0czsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) +- [TestMultiCall.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IFRlc3RNdWx0aUNhbGwgewogICAgZnVuY3Rpb24gdGVzdCh1aW50IF9pKSBleHRlcm5hbCBwdXJlIHJldHVybnMgKHVpbnQpIHsKICAgICAgICByZXR1cm4gX2k7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0RGF0YSh1aW50IF9pKSBleHRlcm5hbCBwdXJlIHJldHVybnMgKGJ5dGVzIG1lbW9yeSkgewogICAgICAgIHJldHVybiBhYmkuZW5jb2RlV2l0aFNlbGVjdG9yKHRoaXMudGVzdC5zZWxlY3RvciwgX2kpOwogICAgfQp9&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/multi-delegatecall.md b/src/exemplos/aplicacoes/multi-delegatecall.md new file mode 100644 index 0000000..a2ae269 --- /dev/null +++ b/src/exemplos/aplicacoes/multi-delegatecall.md @@ -0,0 +1,72 @@ +# Multi Delegatecall + +Um exemplo de chamada de várias funções com uma única transação, usando `delegatecall`. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract MultiDelegatecall { + error DelegatecallFailed(); + + function multiDelegatecall(bytes[] memory data) + external + payable + returns (bytes[] memory results) + { + results = new bytes[](data.length); + + for (uint i; i < data.length; i++) { + (bool ok, bytes memory res) = address(this).delegatecall(data[i]); + if (!ok) { + revert DelegatecallFailed(); + } + results[i] = res; + } + } +} + +// Por que usar a chamada multi delegatecall? Por que não multi call? +// alice -> multi call --- chama ---> test (msg.sender = multi call) +// alice -> test --- delegatecall ---> test (msg.sender = alice) +contract TestMultiDelegatecall is MultiDelegatecall { + event Log(address caller, string func, uint i); + + function func1(uint x, uint y) external { + // msg.sender = alice + emit Log(msg.sender, "func1", x + y); + } + + function func2() external returns (uint) { + // msg.sender = alice + emit Log(msg.sender, "func2", 2); + return 111; + } + + mapping(address => uint) public balanceOf; + + // AVISO: código inseguro quando usado em combinação com multi-delegatecall + // o usuário pode cunhar várias vezes pelo preço de msg.value + function mint() external payable { + balanceOf[msg.sender] += msg.value; + } +} + +contract Helper { + function getFunc1Data(uint x, uint y) external pure returns (bytes memory) { + return abi.encodeWithSelector(TestMultiDelegatecall.func1.selector, x, y); + } + + function getFunc2Data() external pure returns (bytes memory) { + return abi.encodeWithSelector(TestMultiDelegatecall.func2.selector); + } + + function getMintData() external pure returns (bytes memory) { + return abi.encodeWithSelector(TestMultiDelegatecall.mint.selector); + } +} +``` + +## Teste no Remix + +- [MultiDelegatecall.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmNvbnRyYWN0IE11bHRpRGVsZWdhdGVjYWxsIHsKICAgIGVycm9yIERlbGVnYXRlY2FsbEZhaWxlZCgpOwoKICAgIGZ1bmN0aW9uIG11bHRpRGVsZWdhdGVjYWxsKGJ5dGVzW10gbWVtb3J5IGRhdGEpCiAgICAgICAgZXh0ZXJuYWwKICAgICAgICBwYXlhYmxlCiAgICAgICAgcmV0dXJucyAoYnl0ZXNbXSBtZW1vcnkgcmVzdWx0cykKICAgIHsKICAgICAgICByZXN1bHRzID0gbmV3IGJ5dGVzW10oZGF0YS5sZW5ndGgpOwoKICAgICAgICBmb3IgKHVpbnQgaTsgaSA8IGRhdGEubGVuZ3RoOyBpKyspIHsKICAgICAgICAgICAgKGJvb2wgb2ssIGJ5dGVzIG1lbW9yeSByZXMpID0gYWRkcmVzcyh0aGlzKS5kZWxlZ2F0ZWNhbGwoZGF0YVtpXSk7CiAgICAgICAgICAgIGlmICghb2spIHsKICAgICAgICAgICAgICAgIHJldmVydCBEZWxlZ2F0ZWNhbGxGYWlsZWQoKTsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXN1bHRzW2ldID0gcmVzOwogICAgICAgIH0KICAgIH0KfQoKLy8gUG9yIHF1ZSB1c2FyIGEgY2hhbWFkYSBtdWx0aSBkZWxlZ2F0ZWNhbGw/IFBvciBxdWUgbmFvIG11bHRpIGNhbGw/Ci8vIGFsaWNlIC0+IG11bHRpIGNhbGwgLS0tIGNoYW1hIC0tLT4gdGVzdCAobXNnLnNlbmRlciA9IG11bHRpIGNhbGwpCi8vIGFsaWNlIC0+IHRlc3QgLS0tIGRlbGVnYXRlY2FsbCAtLS0+IHRlc3QgKG1zZy5zZW5kZXIgPSBhbGljZSkKY29udHJhY3QgVGVzdE11bHRpRGVsZWdhdGVjYWxsIGlzIE11bHRpRGVsZWdhdGVjYWxsIHsKICAgIGV2ZW50IExvZyhhZGRyZXNzIGNhbGxlciwgc3RyaW5nIGZ1bmMsIHVpbnQgaSk7CgogICAgZnVuY3Rpb24gZnVuYzEodWludCB4LCB1aW50IHkpIGV4dGVybmFsIHsKICAgICAgICAvLyBtc2cuc2VuZGVyID0gYWxpY2UKICAgICAgICBlbWl0IExvZyhtc2cuc2VuZGVyLCAiZnVuYzEiLCB4ICsgeSk7CiAgICB9CgogICAgZnVuY3Rpb24gZnVuYzIoKSBleHRlcm5hbCByZXR1cm5zICh1aW50KSB7CiAgICAgICAgLy8gbXNnLnNlbmRlciA9IGFsaWNlCiAgICAgICAgZW1pdCBMb2cobXNnLnNlbmRlciwgImZ1bmMyIiwgMik7CiAgICAgICAgcmV0dXJuIDExMTsKICAgIH0KCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gdWludCkgcHVibGljIGJhbGFuY2VPZjsKCiAgICAvLyBBVklTTzogY29kaWdvIGluc2VndXJvIHF1YW5kbyB1c2FkbyBlbSBjb21iaW5hY2FvIGNvbSBtdWx0aS1kZWxlZ2F0ZWNhbGwKICAgIC8vIG8gdXN1YXJpbyBwb2RlIGN1bmhhciB2YXJpYXMgdmV6ZXMgcGVsbyBwcmVjbyBkZSBtc2cudmFsdWUKICAgIGZ1bmN0aW9uIG1pbnQoKSBleHRlcm5hbCBwYXlhYmxlIHsKICAgICAgICBiYWxhbmNlT2ZbbXNnLnNlbmRlcl0gKz0gbXNnLnZhbHVlOwogICAgfQp9Cgpjb250cmFjdCBIZWxwZXIgewogICAgZnVuY3Rpb24gZ2V0RnVuYzFEYXRhKHVpbnQgeCwgdWludCB5KSBleHRlcm5hbCBwdXJlIHJldHVybnMgKGJ5dGVzIG1lbW9yeSkgewogICAgICAgIHJldHVybiBhYmkuZW5jb2RlV2l0aFNlbGVjdG9yKFRlc3RNdWx0aURlbGVnYXRlY2FsbC5mdW5jMS5zZWxlY3RvciwgeCwgeSk7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0RnVuYzJEYXRhKCkgZXh0ZXJuYWwgcHVyZSByZXR1cm5zIChieXRlcyBtZW1vcnkpIHsKICAgICAgICByZXR1cm4gYWJpLmVuY29kZVdpdGhTZWxlY3RvcihUZXN0TXVsdGlEZWxlZ2F0ZWNhbGwuZnVuYzIuc2VsZWN0b3IpOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldE1pbnREYXRhKCkgZXh0ZXJuYWwgcHVyZSByZXR1cm5zIChieXRlcyBtZW1vcnkpIHsKICAgICAgICByZXR1cm4gYWJpLmVuY29kZVdpdGhTZWxlY3RvcihUZXN0TXVsdGlEZWxlZ2F0ZWNhbGwubWludC5zZWxlY3Rvcik7CiAgICB9Cn0=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/proxy-atualizavel.md b/src/exemplos/aplicacoes/proxy-atualizavel.md new file mode 100644 index 0000000..85655da --- /dev/null +++ b/src/exemplos/aplicacoes/proxy-atualizavel.md @@ -0,0 +1,262 @@ +# Proxy atualizável + +Exemplo de contrato de proxy atualizável. Nunca use isso em produção. + +Este exemplo mostra + +- como usar `delegatecalle` retornar dados quando `fallback` é chamado. +- como armazenar o endereço de `admin` e `implementation` em um slot específico. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Padrão de proxy atualizável transparente + +contract CounterV1 { + uint public count; + + function inc() external { + count += 1; + } +} + +contract CounterV2 { + uint public count; + + function inc() external { + count += 1; + } + + function dec() external { + count -= 1; + } +} + +contract BuggyProxy { + address public implementation; + address public admin; + + constructor() { + admin = msg.sender; + } + + function _delegate() private { + (bool ok, bytes memory res) = implementation.delegatecall(msg.data); + require(ok, "delegatecall failed"); + } + + fallback() external payable { + _delegate(); + } + + receive() external payable { + _delegate(); + } + + function upgradeTo(address _implementation) external { + require(msg.sender == admin, "not authorized"); + implementation = _implementation; + } +} + +contract Dev { + function selectors() + external + view + returns ( + bytes4, + bytes4, + bytes4 + ) + { + return ( + Proxy.admin.selector, + Proxy.implementation.selector, + Proxy.upgradeTo.selector + ); + } +} + +contract Proxy { + // Todas as funções/variáveis ​​devem ser privadas, encaminhe todas as chamadas para fallback + + // -1 para pré-imagem desconhecida + // 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc + bytes32 private constant IMPLEMENTATION_SLOT = + bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1); + // 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103 + bytes32 private constant ADMIN_SLOT = + bytes32(uint(keccak256("eip1967.proxy.admin")) - 1); + + constructor() { + _setAdmin(msg.sender); + } + + modifier ifAdmin() { + if (msg.sender == _getAdmin()) { + _; + } else { + _fallback(); + } + } + + function _getAdmin() private view returns (address) { + return StorageSlot.getAddressSlot(ADMIN_SLOT).value; + } + + function _setAdmin(address _admin) private { + require(_admin != address(0), "admin = zero address"); + StorageSlot.getAddressSlot(ADMIN_SLOT).value = _admin; + } + + function _getImplementation() private view returns (address) { + return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; + } + + function _setImplementation(address _implementation) private { + require(_implementation.code.length > 0, "implementation is not contract"); + StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = _implementation; + } + + // Interface do administrador// + function changeAdmin(address _admin) external ifAdmin { + _setAdmin(_admin); + } + + // 0x3659cfe6 + function upgradeTo(address _implementation) external ifAdmin { + _setImplementation(_implementation); + } + + // 0xf851a440 + function admin() external ifAdmin returns (address) { + return _getAdmin(); + } + + // 0x5c60da1b + function implementation() external ifAdmin returns (address) { + return _getImplementation(); + } + + // Interface do usuário // + function _delegate(address _implementation) internal virtual { + assembly { + // Copia msg.data. Assumimos o controle total da memória nesta montagem em linha + // bloquear porque não retornará ao código Solidity. Nós sobrescrevemos o + // Nós sobrescrevemos o bloco de rascunho de Solidity na posição de memória 0. + + + // calldatacopy(t, f, s) - copia S bytes de calldata na posição f para mem na posição t + // calldatasize() - tamanho dos dados da chamada em bytes + calldatacopy(0, 0, calldatasize()) + + // Chama a implementação. + // out and outsize são 0 porque ainda não sabemos o tamanho. + + // delegatecall(g, a, in, insize, out, outsize) - + // - contrato de chamada no endereço a + // - com entrada mem[in…(in+insize)) + // - fornecimento de gás g + // - área de saída mem[out…(out+outsize)) + // - retornando 0 em caso de erro (por exemplo, falta de gás) e 1 em caso de sucesso + let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) + + // Copie os dados retornados. + // returndatacopy(t, f, s) - copia S bytes de returndata na posição f para mem na posição t + // returndatasize() - tamanho do último returndata + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall retorna 0 em caso de erro. + case 0 { + // revert(p, s) - finaliza a execução, reverte mudanças de estado, retorna dados mem[p…(p+s)) + revert(0, returndatasize()) + } + default { + // return(p, s) - finaliza a execução, retorna dados mem[p…(p+s)) + return(0, returndatasize()) + } + } + } + + function _fallback() private { + _delegate(_getImplementation()); + } + + fallback() external payable { + _fallback(); + } + + receive() external payable { + _fallback(); + } +} + +contract ProxyAdmin { + address public owner; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "not owner"); + _; + } + + function getProxyAdmin(address proxy) external view returns (address) { + (bool ok, bytes memory res) = proxy.staticcall( + abi.encodeCall(Proxy.implementation, ()) + ); + require(ok, "call failed"); + return abi.decode(res, (address)); + } + + function getProxyImplementation(address proxy) external view returns (address) { + (bool ok, bytes memory res) = proxy.staticcall(abi.encodeCall(Proxy.admin, ())); + require(ok, "call failed"); + return abi.decode(res, (address)); + } + + function changeProxyAdmin(address payable proxy, address admin) external onlyOwner { + Proxy(proxy).changeAdmin(admin); + } + + function upgrade(address payable proxy, address implementation) external onlyOwner { + Proxy(proxy).upgradeTo(implementation); + } +} + +library StorageSlot { + struct AddressSlot { + address value; + } + + function getAddressSlot(bytes32 slot) + internal + pure + returns (AddressSlot storage r) + { + assembly { + r.slot := slot + } + } +} + +contract TestSlot { + bytes32 public constant slot = keccak256("TEST_SLOT"); + + function getSlot() external view returns (address) { + return StorageSlot.getAddressSlot(slot).value; + } + + function writeSlot(address _addr) external { + StorageSlot.getAddressSlot(slot).value = _addr; + } +} +``` + +## Teste no Remix + +- [UpgradeableProxy.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCi8vIFBhZHJhbyBkZSBwcm94eSBhdHVhbGl6YXZlbCB0cmFuc3BhcmVudGUKCmNvbnRyYWN0IENvdW50ZXJWMSB7CiAgICB1aW50IHB1YmxpYyBjb3VudDsKCiAgICBmdW5jdGlvbiBpbmMoKSBleHRlcm5hbCB7CiAgICAgICAgY291bnQgKz0gMTsKICAgIH0KfQoKY29udHJhY3QgQ291bnRlclYyIHsKICAgIHVpbnQgcHVibGljIGNvdW50OwoKICAgIGZ1bmN0aW9uIGluYygpIGV4dGVybmFsIHsKICAgICAgICBjb3VudCArPSAxOwogICAgfQoKICAgIGZ1bmN0aW9uIGRlYygpIGV4dGVybmFsIHsKICAgICAgICBjb3VudCAtPSAxOwogICAgfQp9Cgpjb250cmFjdCBCdWdneVByb3h5IHsKICAgIGFkZHJlc3MgcHVibGljIGltcGxlbWVudGF0aW9uOwogICAgYWRkcmVzcyBwdWJsaWMgYWRtaW47CgogICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgYWRtaW4gPSBtc2cuc2VuZGVyOwogICAgfQoKICAgIGZ1bmN0aW9uIF9kZWxlZ2F0ZSgpIHByaXZhdGUgewogICAgICAgIChib29sIG9rLCBieXRlcyBtZW1vcnkgcmVzKSA9IGltcGxlbWVudGF0aW9uLmRlbGVnYXRlY2FsbChtc2cuZGF0YSk7CiAgICAgICAgcmVxdWlyZShvaywgImRlbGVnYXRlY2FsbCBmYWlsZWQiKTsKICAgIH0KCiAgICBmYWxsYmFjaygpIGV4dGVybmFsIHBheWFibGUgewogICAgICAgIF9kZWxlZ2F0ZSgpOwogICAgfQoKICAgIHJlY2VpdmUoKSBleHRlcm5hbCBwYXlhYmxlIHsKICAgICAgICBfZGVsZWdhdGUoKTsKICAgIH0KCiAgICBmdW5jdGlvbiB1cGdyYWRlVG8oYWRkcmVzcyBfaW1wbGVtZW50YXRpb24pIGV4dGVybmFsIHsKICAgICAgICByZXF1aXJlKG1zZy5zZW5kZXIgPT0gYWRtaW4sICJub3QgYXV0aG9yaXplZCIpOwogICAgICAgIGltcGxlbWVudGF0aW9uID0gX2ltcGxlbWVudGF0aW9uOwogICAgfQp9Cgpjb250cmFjdCBEZXYgewogICAgZnVuY3Rpb24gc2VsZWN0b3JzKCkKICAgICAgICBleHRlcm5hbAogICAgICAgIHZpZXcKICAgICAgICByZXR1cm5zICgKICAgICAgICAgICAgYnl0ZXM0LAogICAgICAgICAgICBieXRlczQsCiAgICAgICAgICAgIGJ5dGVzNAogICAgICAgICkKICAgIHsKICAgICAgICByZXR1cm4gKAogICAgICAgICAgICBQcm94eS5hZG1pbi5zZWxlY3RvciwKICAgICAgICAgICAgUHJveHkuaW1wbGVtZW50YXRpb24uc2VsZWN0b3IsCiAgICAgICAgICAgIFByb3h5LnVwZ3JhZGVUby5zZWxlY3RvcgogICAgICAgICk7CiAgICB9Cn0KCmNvbnRyYWN0IFByb3h5IHsKICAgIC8vIFRvZGFzIGFzIGZ1bmNvZXMvdmFyaWF2ZWlzID8/ZGV2ZW0gc2VyIHByaXZhZGFzLCBlbmNhbWluaGUgdG9kYXMgYXMgY2hhbWFkYXMgcGFyYSBmYWxsYmFjawoKICAgIC8vIC0xIHBhcmEgcHJlLWltYWdlbSBkZXNjb25oZWNpZGEKICAgIC8vIDB4MzYwODk0YTEzYmExYTMyMTA2NjdjODI4NDkyZGI5OGRjYTNlMjA3NmNjMzczNWE5MjBhM2NhNTA1ZDM4MmJiYwogICAgYnl0ZXMzMiBwcml2YXRlIGNvbnN0YW50IElNUExFTUVOVEFUSU9OX1NMT1QgPQogICAgICAgIGJ5dGVzMzIodWludChrZWNjYWsyNTYoImVpcDE5NjcucHJveHkuaW1wbGVtZW50YXRpb24iKSkgLSAxKTsKICAgIC8vIDB4YjUzMTI3Njg0YTU2OGIzMTczYWUxM2I5ZjhhNjAxNmUyNDNlNjNiNmU4ZWUxMTc4ZDZhNzE3ODUwYjVkNjEwMwogICAgYnl0ZXMzMiBwcml2YXRlIGNvbnN0YW50IEFETUlOX1NMT1QgPQogICAgICAgIGJ5dGVzMzIodWludChrZWNjYWsyNTYoImVpcDE5NjcucHJveHkuYWRtaW4iKSkgLSAxKTsKCiAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgICBfc2V0QWRtaW4obXNnLnNlbmRlcik7CiAgICB9CgogICAgbW9kaWZpZXIgaWZBZG1pbigpIHsKICAgICAgICBpZiAobXNnLnNlbmRlciA9PSBfZ2V0QWRtaW4oKSkgewogICAgICAgICAgICBfOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIF9mYWxsYmFjaygpOwogICAgICAgIH0KICAgIH0KCiAgICBmdW5jdGlvbiBfZ2V0QWRtaW4oKSBwcml2YXRlIHZpZXcgcmV0dXJucyAoYWRkcmVzcykgewogICAgICAgIHJldHVybiBTdG9yYWdlU2xvdC5nZXRBZGRyZXNzU2xvdChBRE1JTl9TTE9UKS52YWx1ZTsKICAgIH0KCiAgICBmdW5jdGlvbiBfc2V0QWRtaW4oYWRkcmVzcyBfYWRtaW4pIHByaXZhdGUgewogICAgICAgIHJlcXVpcmUoX2FkbWluICE9IGFkZHJlc3MoMCksICJhZG1pbiA9IHplcm8gYWRkcmVzcyIpOwogICAgICAgIFN0b3JhZ2VTbG90LmdldEFkZHJlc3NTbG90KEFETUlOX1NMT1QpLnZhbHVlID0gX2FkbWluOwogICAgfQoKICAgIGZ1bmN0aW9uIF9nZXRJbXBsZW1lbnRhdGlvbigpIHByaXZhdGUgdmlldyByZXR1cm5zIChhZGRyZXNzKSB7CiAgICAgICAgcmV0dXJuIFN0b3JhZ2VTbG90LmdldEFkZHJlc3NTbG90KElNUExFTUVOVEFUSU9OX1NMT1QpLnZhbHVlOwogICAgfQoKICAgIGZ1bmN0aW9uIF9zZXRJbXBsZW1lbnRhdGlvbihhZGRyZXNzIF9pbXBsZW1lbnRhdGlvbikgcHJpdmF0ZSB7CiAgICAgICAgcmVxdWlyZShfaW1wbGVtZW50YXRpb24uY29kZS5sZW5ndGggPiAwLCAiaW1wbGVtZW50YXRpb24gaXMgbm90IGNvbnRyYWN0Iik7CiAgICAgICAgU3RvcmFnZVNsb3QuZ2V0QWRkcmVzc1Nsb3QoSU1QTEVNRU5UQVRJT05fU0xPVCkudmFsdWUgPSBfaW1wbGVtZW50YXRpb247CiAgICB9CgogICAgLy8gSW50ZXJmYWNlIGRvIGFkbWluaXN0cmFkb3IvLwogICAgZnVuY3Rpb24gY2hhbmdlQWRtaW4oYWRkcmVzcyBfYWRtaW4pIGV4dGVybmFsIGlmQWRtaW4gewogICAgICAgIF9zZXRBZG1pbihfYWRtaW4pOwogICAgfQoKICAgIC8vIDB4MzY1OWNmZTYKICAgIGZ1bmN0aW9uIHVwZ3JhZGVUbyhhZGRyZXNzIF9pbXBsZW1lbnRhdGlvbikgZXh0ZXJuYWwgaWZBZG1pbiB7CiAgICAgICAgX3NldEltcGxlbWVudGF0aW9uKF9pbXBsZW1lbnRhdGlvbik7CiAgICB9CgogICAgLy8gMHhmODUxYTQ0MAogICAgZnVuY3Rpb24gYWRtaW4oKSBleHRlcm5hbCBpZkFkbWluIHJldHVybnMgKGFkZHJlc3MpIHsKICAgICAgICByZXR1cm4gX2dldEFkbWluKCk7CiAgICB9CgogICAgLy8gMHg1YzYwZGExYgogICAgZnVuY3Rpb24gaW1wbGVtZW50YXRpb24oKSBleHRlcm5hbCBpZkFkbWluIHJldHVybnMgKGFkZHJlc3MpIHsKICAgICAgICByZXR1cm4gX2dldEltcGxlbWVudGF0aW9uKCk7CiAgICB9CgogICAgLy8gSW50ZXJmYWNlIGRvIHVzdWFyaW8gLy8KICAgIGZ1bmN0aW9uIF9kZWxlZ2F0ZShhZGRyZXNzIF9pbXBsZW1lbnRhdGlvbikgaW50ZXJuYWwgdmlydHVhbCB7CiAgICAgICAgYXNzZW1ibHkgewogICAgICAgICAgICAvLyBDb3BpYSBtc2cuZGF0YS4gQXNzdW1pbW9zIG8gY29udHJvbGUgdG90YWwgZGEgbWVtb3JpYSBuZXN0YSBtb250YWdlbSBlbSBsaW5oYQogICAgICAgICAgICAvLyBibG9xdWVhciBwb3JxdWUgbmFvIHJldG9ybmFyYSBhbyBjb2RpZ28gU29saWRpdHkuIE5vcyBzb2JyZXNjcmV2ZW1vcyBvCiAgICAgICAgICAgIC8vIE5vcyBzb2JyZXNjcmV2ZW1vcyBvIGJsb2NvIGRlIHJhc2N1bmhvIGRlIFNvbGlkaXR5IG5hIHBvc2ljYW8gZGUgbWVtb3JpYSAwLgoKCiAgICAgICAgICAgIC8vIGNhbGxkYXRhY29weSh0LCBmLCBzKSAtIGNvcGlhIFMgYnl0ZXMgZGUgY2FsbGRhdGEgbmEgcG9zaWNhbyBmIHBhcmEgbWVtIG5hIHBvc2ljYW8gdAogICAgICAgICAgICAvLyBjYWxsZGF0YXNpemUoKSAtIHRhbWFuaG8gZG9zIGRhZG9zIGRhIGNoYW1hZGEgZW0gYnl0ZXMKICAgICAgICAgICAgY2FsbGRhdGFjb3B5KDAsIDAsIGNhbGxkYXRhc2l6ZSgpKQoKICAgICAgICAgICAgLy8gQ2hhbWEgYSBpbXBsZW1lbnRhY2FvLgogICAgICAgICAgICAvLyBvdXQgYW5kIG91dHNpemUgc2FvIDAgcG9ycXVlIGFpbmRhIG5hbyBzYWJlbW9zIG8gdGFtYW5oby4KCiAgICAgICAgICAgIC8vIGRlbGVnYXRlY2FsbChnLCBhLCBpbiwgaW5zaXplLCBvdXQsIG91dHNpemUpIC0KICAgICAgICAgICAgLy8gLSBjb250cmF0byBkZSBjaGFtYWRhIG5vIGVuZGVyZWNvIGEKICAgICAgICAgICAgLy8gLSBjb20gZW50cmFkYSBtZW1baW4/KGluK2luc2l6ZSkpCiAgICAgICAgICAgIC8vIC0gZm9ybmVjaW1lbnRvIGRlIGdhcyBnCiAgICAgICAgICAgIC8vIC0gYXJlYSBkZSBzYWlkYSBtZW1bb3V0PyhvdXQrb3V0c2l6ZSkpCiAgICAgICAgICAgIC8vIC0gcmV0b3JuYW5kbyAwIGVtIGNhc28gZGUgZXJybyAocG9yIGV4ZW1wbG8sIGZhbHRhIGRlIGdhcykgZSAxIGVtIGNhc28gZGUgc3VjZXNzbwogICAgICAgICAgICBsZXQgcmVzdWx0IDo9IGRlbGVnYXRlY2FsbChnYXMoKSwgX2ltcGxlbWVudGF0aW9uLCAwLCBjYWxsZGF0YXNpemUoKSwgMCwgMCkKCiAgICAgICAgICAgIC8vIENvcGllIG9zIGRhZG9zIHJldG9ybmFkb3MuCiAgICAgICAgICAgIC8vIHJldHVybmRhdGFjb3B5KHQsIGYsIHMpIC0gY29waWEgUyBieXRlcyBkZSByZXR1cm5kYXRhIG5hIHBvc2ljYW8gZiBwYXJhIG1lbSBuYSBwb3NpY2FvIHQKICAgICAgICAgICAgLy8gcmV0dXJuZGF0YXNpemUoKSAtIHRhbWFuaG8gZG8gdWx0aW1vIHJldHVybmRhdGEKICAgICAgICAgICAgcmV0dXJuZGF0YWNvcHkoMCwgMCwgcmV0dXJuZGF0YXNpemUoKSkKCiAgICAgICAgICAgIHN3aXRjaCByZXN1bHQKICAgICAgICAgICAgLy8gZGVsZWdhdGVjYWxsIHJldG9ybmEgMCBlbSBjYXNvIGRlIGVycm8uCiAgICAgICAgICAgIGNhc2UgMCB7CiAgICAgICAgICAgICAgICAvLyByZXZlcnQocCwgcykgLSBmaW5hbGl6YSBhIGV4ZWN1Y2FvLCByZXZlcnRlIG11ZGFuY2FzIGRlIGVzdGFkbywgcmV0b3JuYSBkYWRvcyBtZW1bcD8ocCtzKSkKICAgICAgICAgICAgICAgIHJldmVydCgwLCByZXR1cm5kYXRhc2l6ZSgpKQogICAgICAgICAgICB9CiAgICAgICAgICAgIGRlZmF1bHQgewogICAgICAgICAgICAgICAgLy8gcmV0dXJuKHAsIHMpIC0gZmluYWxpemEgYSBleGVjdWNhbywgcmV0b3JuYSBkYWRvcyBtZW1bcD8ocCtzKSkKICAgICAgICAgICAgICAgIHJldHVybigwLCByZXR1cm5kYXRhc2l6ZSgpKQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIGZ1bmN0aW9uIF9mYWxsYmFjaygpIHByaXZhdGUgewogICAgICAgIF9kZWxlZ2F0ZShfZ2V0SW1wbGVtZW50YXRpb24oKSk7CiAgICB9CgogICAgZmFsbGJhY2soKSBleHRlcm5hbCBwYXlhYmxlIHsKICAgICAgICBfZmFsbGJhY2soKTsKICAgIH0KCiAgICByZWNlaXZlKCkgZXh0ZXJuYWwgcGF5YWJsZSB7CiAgICAgICAgX2ZhbGxiYWNrKCk7CiAgICB9Cn0KCmNvbnRyYWN0IFByb3h5QWRtaW4gewogICAgYWRkcmVzcyBwdWJsaWMgb3duZXI7CgogICAgY29uc3RydWN0b3IoKSB7CiAgICAgICAgb3duZXIgPSBtc2cuc2VuZGVyOwogICAgfQoKICAgIG1vZGlmaWVyIG9ubHlPd25lcigpIHsKICAgICAgICByZXF1aXJlKG1zZy5zZW5kZXIgPT0gb3duZXIsICJub3Qgb3duZXIiKTsKICAgICAgICBfOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldFByb3h5QWRtaW4oYWRkcmVzcyBwcm94eSkgZXh0ZXJuYWwgdmlldyByZXR1cm5zIChhZGRyZXNzKSB7CiAgICAgICAgKGJvb2wgb2ssIGJ5dGVzIG1lbW9yeSByZXMpID0gcHJveHkuc3RhdGljY2FsbCgKICAgICAgICAgICAgYWJpLmVuY29kZUNhbGwoUHJveHkuaW1wbGVtZW50YXRpb24sICgpKQogICAgICAgICk7CiAgICAgICAgcmVxdWlyZShvaywgImNhbGwgZmFpbGVkIik7CiAgICAgICAgcmV0dXJuIGFiaS5kZWNvZGUocmVzLCAoYWRkcmVzcykpOwogICAgfQoKICAgIGZ1bmN0aW9uIGdldFByb3h5SW1wbGVtZW50YXRpb24oYWRkcmVzcyBwcm94eSkgZXh0ZXJuYWwgdmlldyByZXR1cm5zIChhZGRyZXNzKSB7CiAgICAgICAgKGJvb2wgb2ssIGJ5dGVzIG1lbW9yeSByZXMpID0gcHJveHkuc3RhdGljY2FsbChhYmkuZW5jb2RlQ2FsbChQcm94eS5hZG1pbiwgKCkpKTsKICAgICAgICByZXF1aXJlKG9rLCAiY2FsbCBmYWlsZWQiKTsKICAgICAgICByZXR1cm4gYWJpLmRlY29kZShyZXMsIChhZGRyZXNzKSk7CiAgICB9CgogICAgZnVuY3Rpb24gY2hhbmdlUHJveHlBZG1pbihhZGRyZXNzIHBheWFibGUgcHJveHksIGFkZHJlc3MgYWRtaW4pIGV4dGVybmFsIG9ubHlPd25lciB7CiAgICAgICAgUHJveHkocHJveHkpLmNoYW5nZUFkbWluKGFkbWluKTsKICAgIH0KCiAgICBmdW5jdGlvbiB1cGdyYWRlKGFkZHJlc3MgcGF5YWJsZSBwcm94eSwgYWRkcmVzcyBpbXBsZW1lbnRhdGlvbikgZXh0ZXJuYWwgb25seU93bmVyIHsKICAgICAgICBQcm94eShwcm94eSkudXBncmFkZVRvKGltcGxlbWVudGF0aW9uKTsKICAgIH0KfQoKbGlicmFyeSBTdG9yYWdlU2xvdCB7CiAgICBzdHJ1Y3QgQWRkcmVzc1Nsb3QgewogICAgICAgIGFkZHJlc3MgdmFsdWU7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0QWRkcmVzc1Nsb3QoYnl0ZXMzMiBzbG90KQogICAgICAgIGludGVybmFsCiAgICAgICAgcHVyZQogICAgICAgIHJldHVybnMgKEFkZHJlc3NTbG90IHN0b3JhZ2UgcikKICAgIHsKICAgICAgICBhc3NlbWJseSB7CiAgICAgICAgICAgIHIuc2xvdCA6PSBzbG90CiAgICAgICAgfQogICAgfQp9Cgpjb250cmFjdCBUZXN0U2xvdCB7CiAgICBieXRlczMyIHB1YmxpYyBjb25zdGFudCBzbG90ID0ga2VjY2FrMjU2KCJURVNUX1NMT1QiKTsKCiAgICBmdW5jdGlvbiBnZXRTbG90KCkgZXh0ZXJuYWwgdmlldyByZXR1cm5zIChhZGRyZXNzKSB7CiAgICAgICAgcmV0dXJuIFN0b3JhZ2VTbG90LmdldEFkZHJlc3NTbG90KHNsb3QpLnZhbHVlOwogICAgfQoKICAgIGZ1bmN0aW9uIHdyaXRlU2xvdChhZGRyZXNzIF9hZGRyKSBleHRlcm5hbCB7CiAgICAgICAgU3RvcmFnZVNsb3QuZ2V0QWRkcmVzc1Nsb3Qoc2xvdCkudmFsdWUgPSBfYWRkcjsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/transferencia-de-token-sem-gas.md b/src/exemplos/aplicacoes/transferencia-de-token-sem-gas.md new file mode 100644 index 0000000..82af838 --- /dev/null +++ b/src/exemplos/aplicacoes/transferencia-de-token-sem-gas.md @@ -0,0 +1,309 @@ +# Transferência de token sem gás + +Transferência de tokens ERC20 sem gás com transação Meta + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IERC20Permit { + function totalSupply() external view returns (uint256); + + function balanceOf(address account) external view returns (uint256); + + function transfer(address recipient, uint256 amount) external returns (bool); + + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 amount) external returns (bool); + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +contract GaslessTokenTransfer { + function send( + address token, + address sender, + address receiver, + uint256 amount, + uint256 fee, + uint256 deadline, + // Assinatura da autorização + uint8 v, + bytes32 r, + bytes32 s + ) external { + // Autorização + IERC20Permit(token).permit( + sender, + address(this), + amount + fee, + deadline, + v, + r, + s + ); + // Enviar o valor ao destinatário + IERC20Permit(token).transferFrom(sender, receiver, amount); + // Aceitar taxa - enviar taxa para msg.sender + IERC20Permit(token).transferFrom(sender, msg.sender, fee); + } +} +``` + +Exemplo de `ERC20` que implementa a autorização copiada do solmate + +```solidity +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. +/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) +/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) +/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. +abstract contract ERC20 { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public name; + + string public symbol; + + uint8 public immutable decimals; + + /*////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*////////////////////////////////////////////////////////////// + EIP-2612 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 internal immutable INITIAL_CHAIN_ID; + + bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; + + mapping(address => uint256) public nonces; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(string memory _name, string memory _symbol, uint8 _decimals) { + name = _name; + symbol = _symbol; + decimals = _decimals; + + INITIAL_CHAIN_ID = block.chainid; + INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); + } + + /*////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 amount) public virtual returns (bool) { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + + return true; + } + + function transfer(address to, uint256 amount) public virtual returns (bool) { + balanceOf[msg.sender] -= amount; + + // Não pode ultrapassar o limite porque a soma de todos os usuários + // Não pode exceder o valor máximo de uint256. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + + return true; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual returns (bool) { + uint256 allowed = allowance[from][msg.sender]; // Poupa gás para aprovações limitadas. + + if (allowed != type(uint256).max) + allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + // Não pode ultrapassar o limite porque a soma de todos os usuários + // Não pode exceder o valor máximo de uint256. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + + return true; + } + + /*////////////////////////////////////////////////////////////// + EIP-2612 LOGIC + //////////////////////////////////////////////////////////////*/ + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual { + require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); + + // Unchecked because the only math done is incrementing + // the owner's nonce which cannot realistically overflow. + + // Não verificado porque a única matemática feita é o incremento do + // o nonce do proprietário, que não pode realisticamente ultrapassar. + unchecked { + address recoveredAddress = ecrecover( + keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ), + owner, + spender, + value, + nonces[owner]++, + deadline + ) + ) + ) + ), + v, + r, + s + ); + + require( + recoveredAddress != address(0) && recoveredAddress == owner, + "INVALID_SIGNER" + ); + + allowance[recoveredAddress][spender] = value; + } + + emit Approval(owner, spender, value); + } + + function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { + return + block.chainid == INITIAL_CHAIN_ID + ? INITIAL_DOMAIN_SEPARATOR + : computeDomainSeparator(); + } + + function computeDomainSeparator() internal view virtual returns (bytes32) { + return + keccak256( + abi.encode( + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + keccak256(bytes(name)), + keccak256("1"), + block.chainid, + address(this) + ) + ); + } + + /*////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint(address to, uint256 amount) internal virtual { + totalSupply += amount; + + // Não pode ultrapassar o limite porque a soma de todos os usuários + // Não pode exceder o valor máximo de uint256. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(address(0), to, amount); + } + + function _burn(address from, uint256 amount) internal virtual { + balanceOf[from] -= amount; + + // Cannot underflow because a user's balance + // will never be larger than the total supply. + + // Não é possível underflow porque o saldo do usuário + // nunca será maior do que o suprimento total. + unchecked { + totalSupply -= amount; + } + + emit Transfer(from, address(0), amount); + } +} + +contract ERC20Permit is ERC20 { + constructor( + string memory _name, + string memory _symbol, + uint8 _decimals + ) ERC20(_name, _symbol, _decimals) {} + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } +} +``` + +## Teste no Remix + +- [ERC20Permit.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmludGVyZmFjZSBJRVJDMjBQZXJtaXQgewogICAgZnVuY3Rpb24gdG90YWxTdXBwbHkoKSBleHRlcm5hbCB2aWV3IHJldHVybnMgKHVpbnQyNTYpOwoKICAgIGZ1bmN0aW9uIGJhbGFuY2VPZihhZGRyZXNzIGFjY291bnQpIGV4dGVybmFsIHZpZXcgcmV0dXJucyAodWludDI1Nik7CgogICAgZnVuY3Rpb24gdHJhbnNmZXIoYWRkcmVzcyByZWNpcGllbnQsIHVpbnQyNTYgYW1vdW50KSBleHRlcm5hbCByZXR1cm5zIChib29sKTsKCiAgICBmdW5jdGlvbiBhbGxvd2FuY2UoYWRkcmVzcyBvd25lciwgYWRkcmVzcyBzcGVuZGVyKSBleHRlcm5hbCB2aWV3IHJldHVybnMgKHVpbnQyNTYpOwoKICAgIGZ1bmN0aW9uIGFwcHJvdmUoYWRkcmVzcyBzcGVuZGVyLCB1aW50MjU2IGFtb3VudCkgZXh0ZXJuYWwgcmV0dXJucyAoYm9vbCk7CgogICAgZnVuY3Rpb24gdHJhbnNmZXJGcm9tKAogICAgICAgIGFkZHJlc3Mgc2VuZGVyLAogICAgICAgIGFkZHJlc3MgcmVjaXBpZW50LAogICAgICAgIHVpbnQyNTYgYW1vdW50CiAgICApIGV4dGVybmFsIHJldHVybnMgKGJvb2wpOwoKICAgIGZ1bmN0aW9uIHBlcm1pdCgKICAgICAgICBhZGRyZXNzIG93bmVyLAogICAgICAgIGFkZHJlc3Mgc3BlbmRlciwKICAgICAgICB1aW50MjU2IHZhbHVlLAogICAgICAgIHVpbnQyNTYgZGVhZGxpbmUsCiAgICAgICAgdWludDggdiwKICAgICAgICBieXRlczMyIHIsCiAgICAgICAgYnl0ZXMzMiBzCiAgICApIGV4dGVybmFsOwoKICAgIGV2ZW50IFRyYW5zZmVyKGFkZHJlc3MgaW5kZXhlZCBmcm9tLCBhZGRyZXNzIGluZGV4ZWQgdG8sIHVpbnQyNTYgdmFsdWUpOwogICAgZXZlbnQgQXBwcm92YWwoYWRkcmVzcyBpbmRleGVkIG93bmVyLCBhZGRyZXNzIGluZGV4ZWQgc3BlbmRlciwgdWludDI1NiB2YWx1ZSk7Cn0KCmNvbnRyYWN0IEdhc2xlc3NUb2tlblRyYW5zZmVyIHsKICAgIGZ1bmN0aW9uIHNlbmQoCiAgICAgICAgYWRkcmVzcyB0b2tlbiwKICAgICAgICBhZGRyZXNzIHNlbmRlciwKICAgICAgICBhZGRyZXNzIHJlY2VpdmVyLAogICAgICAgIHVpbnQyNTYgYW1vdW50LAogICAgICAgIHVpbnQyNTYgZmVlLAogICAgICAgIHVpbnQyNTYgZGVhZGxpbmUsCiAgICAgICAgLy8gQXNzaW5hdHVyYSBkYSBhdXRvcml6YWNhbwogICAgICAgIHVpbnQ4IHYsCiAgICAgICAgYnl0ZXMzMiByLAogICAgICAgIGJ5dGVzMzIgcwogICAgKSBleHRlcm5hbCB7CiAgICAgICAgLy8gQXV0b3JpemFjYW8KICAgICAgICBJRVJDMjBQZXJtaXQodG9rZW4pLnBlcm1pdCgKICAgICAgICAgICAgc2VuZGVyLAogICAgICAgICAgICBhZGRyZXNzKHRoaXMpLAogICAgICAgICAgICBhbW91bnQgKyBmZWUsCiAgICAgICAgICAgIGRlYWRsaW5lLAogICAgICAgICAgICB2LAogICAgICAgICAgICByLAogICAgICAgICAgICBzCiAgICAgICAgKTsKICAgICAgICAvLyBFbnZpYXIgbyB2YWxvciBhbyBkZXN0aW5hdGFyaW8KICAgICAgICBJRVJDMjBQZXJtaXQodG9rZW4pLnRyYW5zZmVyRnJvbShzZW5kZXIsIHJlY2VpdmVyLCBhbW91bnQpOwogICAgICAgIC8vIEFjZWl0YXIgdGF4YSAtIGVudmlhciB0YXhhIHBhcmEgbXNnLnNlbmRlcgogICAgICAgIElFUkMyMFBlcm1pdCh0b2tlbikudHJhbnNmZXJGcm9tKHNlbmRlciwgbXNnLnNlbmRlciwgZmVlKTsKICAgIH0KfQ=&version=soljson-v0.8.20+commit.a1b79de6.js) + +- [GaslessTokenTransfer.sol](https://remix.ethereum.org/#codeLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkKcHJhZ21hIHNvbGlkaXR5ID49MC44LjA7CgovLy8gQG5vdGljZSBNb2Rlcm4gYW5kIGdhcyBlZmZpY2llbnQgRVJDMjAgKyBFSVAtMjYxMiBpbXBsZW1lbnRhdGlvbi4KLy8vIEBhdXRob3IgU29sbWF0ZSAoaHR0cHM6Ly9naXRodWIuY29tL3RyYW5zbWlzc2lvbnMxMS9zb2xtYXRlL2Jsb2IvbWFpbi9zcmMvdG9rZW5zL0VSQzIwLnNvbCkKLy8vIEBhdXRob3IgTW9kaWZpZWQgZnJvbSBVbmlzd2FwIChodHRwczovL2dpdGh1Yi5jb20vVW5pc3dhcC91bmlzd2FwLXYyLWNvcmUvYmxvYi9tYXN0ZXIvY29udHJhY3RzL1VuaXN3YXBWMkVSQzIwLnNvbCkKLy8vIEBkZXYgRG8gbm90IG1hbnVhbGx5IHNldCBiYWxhbmNlcyB3aXRob3V0IHVwZGF0aW5nIHRvdGFsU3VwcGx5LCBhcyB0aGUgc3VtIG9mIGFsbCB1c2VyIGJhbGFuY2VzIG11c3Qgbm90IGV4Y2VlZCBpdC4KYWJzdHJhY3QgY29udHJhY3QgRVJDMjAgewogICAgLyovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFVkVOVFMKICAgIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vKi8KCiAgICBldmVudCBUcmFuc2ZlcihhZGRyZXNzIGluZGV4ZWQgZnJvbSwgYWRkcmVzcyBpbmRleGVkIHRvLCB1aW50MjU2IGFtb3VudCk7CgogICAgZXZlbnQgQXBwcm92YWwoYWRkcmVzcyBpbmRleGVkIG93bmVyLCBhZGRyZXNzIGluZGV4ZWQgc3BlbmRlciwgdWludDI1NiBhbW91bnQpOwoKICAgIC8qLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1FVEFEQVRBIFNUT1JBR0UKICAgIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vKi8KCiAgICBzdHJpbmcgcHVibGljIG5hbWU7CgogICAgc3RyaW5nIHB1YmxpYyBzeW1ib2w7CgogICAgdWludDggcHVibGljIGltbXV0YWJsZSBkZWNpbWFsczsKCiAgICAvKi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVSQzIwIFNUT1JBR0UKICAgIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vKi8KCiAgICB1aW50MjU2IHB1YmxpYyB0b3RhbFN1cHBseTsKCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gdWludDI1NikgcHVibGljIGJhbGFuY2VPZjsKCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gbWFwcGluZyhhZGRyZXNzID0+IHVpbnQyNTYpKSBwdWJsaWMgYWxsb3dhbmNlOwoKICAgIC8qLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVJUC0yNjEyIFNUT1JBR0UKICAgIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vKi8KCiAgICB1aW50MjU2IGludGVybmFsIGltbXV0YWJsZSBJTklUSUFMX0NIQUlOX0lEOwoKICAgIGJ5dGVzMzIgaW50ZXJuYWwgaW1tdXRhYmxlIElOSVRJQUxfRE9NQUlOX1NFUEFSQVRPUjsKCiAgICBtYXBwaW5nKGFkZHJlc3MgPT4gdWludDI1NikgcHVibGljIG5vbmNlczsKCiAgICAvKi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDT05TVFJVQ1RPUgogICAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8qLwoKICAgIGNvbnN0cnVjdG9yKHN0cmluZyBtZW1vcnkgX25hbWUsIHN0cmluZyBtZW1vcnkgX3N5bWJvbCwgdWludDggX2RlY2ltYWxzKSB7CiAgICAgICAgbmFtZSA9IF9uYW1lOwogICAgICAgIHN5bWJvbCA9IF9zeW1ib2w7CiAgICAgICAgZGVjaW1hbHMgPSBfZGVjaW1hbHM7CgogICAgICAgIElOSVRJQUxfQ0hBSU5fSUQgPSBibG9jay5jaGFpbmlkOwogICAgICAgIElOSVRJQUxfRE9NQUlOX1NFUEFSQVRPUiA9IGNvbXB1dGVEb21haW5TZXBhcmF0b3IoKTsKICAgIH0KCiAgICAvKi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFUkMyMCBMT0dJQwogICAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8qLwoKICAgIGZ1bmN0aW9uIGFwcHJvdmUoYWRkcmVzcyBzcGVuZGVyLCB1aW50MjU2IGFtb3VudCkgcHVibGljIHZpcnR1YWwgcmV0dXJucyAoYm9vbCkgewogICAgICAgIGFsbG93YW5jZVttc2cuc2VuZGVyXVtzcGVuZGVyXSA9IGFtb3VudDsKCiAgICAgICAgZW1pdCBBcHByb3ZhbChtc2cuc2VuZGVyLCBzcGVuZGVyLCBhbW91bnQpOwoKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBmdW5jdGlvbiB0cmFuc2ZlcihhZGRyZXNzIHRvLCB1aW50MjU2IGFtb3VudCkgcHVibGljIHZpcnR1YWwgcmV0dXJucyAoYm9vbCkgewogICAgICAgIGJhbGFuY2VPZlttc2cuc2VuZGVyXSAtPSBhbW91bnQ7CgogICAgICAgIC8vIE7Do28gcG9kZSB1bHRyYXBhc3NhciBvIGxpbWl0ZSBwb3JxdWUgYSBzb21hIGRlIHRvZG9zIG9zIHVzdcOhcmlvcwogICAgICAgIC8vIE7Do28gcG9kZSBleGNlZGVyIG8gdmFsb3IgbcOheGltbyBkZSB1aW50MjU2LgogICAgICAgIHVuY2hlY2tlZCB7CiAgICAgICAgICAgIGJhbGFuY2VPZlt0b10gKz0gYW1vdW50OwogICAgICAgIH0KCiAgICAgICAgZW1pdCBUcmFuc2Zlcihtc2cuc2VuZGVyLCB0bywgYW1vdW50KTsKCiAgICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgZnVuY3Rpb24gdHJhbnNmZXJGcm9tKAogICAgICAgIGFkZHJlc3MgZnJvbSwKICAgICAgICBhZGRyZXNzIHRvLAogICAgICAgIHVpbnQyNTYgYW1vdW50CiAgICApIHB1YmxpYyB2aXJ0dWFsIHJldHVybnMgKGJvb2wpIHsKICAgICAgICB1aW50MjU2IGFsbG93ZWQgPSBhbGxvd2FuY2VbZnJvbV1bbXNnLnNlbmRlcl07IC8vIFBvdXBhIGfDoXMgcGFyYSBhcHJvdmHDp8O1ZXMgbGltaXRhZGFzLgoKICAgICAgICBpZiAoYWxsb3dlZCAhPSB0eXBlKHVpbnQyNTYpLm1heCkKICAgICAgICAgICAgYWxsb3dhbmNlW2Zyb21dW21zZy5zZW5kZXJdID0gYWxsb3dlZCAtIGFtb3VudDsKCiAgICAgICAgYmFsYW5jZU9mW2Zyb21dIC09IGFtb3VudDsKCiAgICAgICAvLyBOw6NvIHBvZGUgdWx0cmFwYXNzYXIgbyBsaW1pdGUgcG9ycXVlIGEgc29tYSBkZSB0b2RvcyBvcyB1c3XDoXJpb3MKICAgICAgIC8vIE7Do28gcG9kZSBleGNlZGVyIG8gdmFsb3IgbcOheGltbyBkZSB1aW50MjU2LgogICAgICAgIHVuY2hlY2tlZCB7CiAgICAgICAgICAgIGJhbGFuY2VPZlt0b10gKz0gYW1vdW50OwogICAgICAgIH0KCiAgICAgICAgZW1pdCBUcmFuc2Zlcihmcm9tLCB0bywgYW1vdW50KTsKCiAgICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgLyovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVJUC0yNjEyIExPR0lDCiAgICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLyovCgogICAgZnVuY3Rpb24gcGVybWl0KAogICAgICAgIGFkZHJlc3Mgb3duZXIsCiAgICAgICAgYWRkcmVzcyBzcGVuZGVyLAogICAgICAgIHVpbnQyNTYgdmFsdWUsCiAgICAgICAgdWludDI1NiBkZWFkbGluZSwKICAgICAgICB1aW50OCB2LAogICAgICAgIGJ5dGVzMzIgciwKICAgICAgICBieXRlczMyIHMKICAgICkgcHVibGljIHZpcnR1YWwgewogICAgICAgIHJlcXVpcmUoZGVhZGxpbmUgPj0gYmxvY2sudGltZXN0YW1wLCAiUEVSTUlUX0RFQURMSU5FX0VYUElSRUQiKTsKCiAgICAgICAgLy8gVW5jaGVja2VkIGJlY2F1c2UgdGhlIG9ubHkgbWF0aCBkb25lIGlzIGluY3JlbWVudGluZwogICAgICAgIC8vIHRoZSBvd25lcidzIG5vbmNlIHdoaWNoIGNhbm5vdCByZWFsaXN0aWNhbGx5IG92ZXJmbG93LgoKICAgICAgICAvLyBOw6NvIHZlcmlmaWNhZG8gcG9ycXVlIGEgw7puaWNhIG1hdGVtw6F0aWNhIGZlaXRhIMOpIG8gaW5jcmVtZW50byBkbwogICAgICAgIC8vIG8gbm9uY2UgZG8gcHJvcHJpZXTDoXJpbywgcXVlIG7Do28gcG9kZSByZWFsaXN0aWNhbWVudGUgdWx0cmFwYXNzYXIuCiAgICAgICAgdW5jaGVja2VkIHsKICAgICAgICAgICAgYWRkcmVzcyByZWNvdmVyZWRBZGRyZXNzID0gZWNyZWNvdmVyKAogICAgICAgICAgICAgICAga2VjY2FrMjU2KAogICAgICAgICAgICAgICAgICAgIGFiaS5lbmNvZGVQYWNrZWQoCiAgICAgICAgICAgICAgICAgICAgICAgICJceDE5XHgwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgIERPTUFJTl9TRVBBUkFUT1IoKSwKICAgICAgICAgICAgICAgICAgICAgICAga2VjY2FrMjU2KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWJpLmVuY29kZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWNjYWsyNTYoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZXJtaXQoYWRkcmVzcyBvd25lcixhZGRyZXNzIHNwZW5kZXIsdWludDI1NiB2YWx1ZSx1aW50MjU2IG5vbmNlLHVpbnQyNTYgZGVhZGxpbmUpIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3duZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BlbmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25jZXNbb3duZXJdKyssCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhZGxpbmUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICB2LAogICAgICAgICAgICAgICAgciwKICAgICAgICAgICAgICAgIHMKICAgICAgICAgICAgKTsKCiAgICAgICAgICAgIHJlcXVpcmUoCiAgICAgICAgICAgICAgICByZWNvdmVyZWRBZGRyZXNzICE9IGFkZHJlc3MoMCkgJiYgcmVjb3ZlcmVkQWRkcmVzcyA9PSBvd25lciwKICAgICAgICAgICAgICAgICJJTlZBTElEX1NJR05FUiIKICAgICAgICAgICAgKTsKCiAgICAgICAgICAgIGFsbG93YW5jZVtyZWNvdmVyZWRBZGRyZXNzXVtzcGVuZGVyXSA9IHZhbHVlOwogICAgICAgIH0KCiAgICAgICAgZW1pdCBBcHByb3ZhbChvd25lciwgc3BlbmRlciwgdmFsdWUpOwogICAgfQoKICAgIGZ1bmN0aW9uIERPTUFJTl9TRVBBUkFUT1IoKSBwdWJsaWMgdmlldyB2aXJ0dWFsIHJldHVybnMgKGJ5dGVzMzIpIHsKICAgICAgICByZXR1cm4KICAgICAgICAgICAgYmxvY2suY2hhaW5pZCA9PSBJTklUSUFMX0NIQUlOX0lECiAgICAgICAgICAgICAgICA/IElOSVRJQUxfRE9NQUlOX1NFUEFSQVRPUgogICAgICAgICAgICAgICAgOiBjb21wdXRlRG9tYWluU2VwYXJhdG9yKCk7CiAgICB9CgogICAgZnVuY3Rpb24gY29tcHV0ZURvbWFpblNlcGFyYXRvcigpIGludGVybmFsIHZpZXcgdmlydHVhbCByZXR1cm5zIChieXRlczMyKSB7CiAgICAgICAgcmV0dXJuCiAgICAgICAgICAgIGtlY2NhazI1NigKICAgICAgICAgICAgICAgIGFiaS5lbmNvZGUoCiAgICAgICAgICAgICAgICAgICAga2VjY2FrMjU2KAogICAgICAgICAgICAgICAgICAgICAgICAiRUlQNzEyRG9tYWluKHN0cmluZyBuYW1lLHN0cmluZyB2ZXJzaW9uLHVpbnQyNTYgY2hhaW5JZCxhZGRyZXNzIHZlcmlmeWluZ0NvbnRyYWN0KSIKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIGtlY2NhazI1NihieXRlcyhuYW1lKSksCiAgICAgICAgICAgICAgICAgICAga2VjY2FrMjU2KCIxIiksCiAgICAgICAgICAgICAgICAgICAgYmxvY2suY2hhaW5pZCwKICAgICAgICAgICAgICAgICAgICBhZGRyZXNzKHRoaXMpCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICk7CiAgICB9CgogICAgLyovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwogICAgICAgICAgICAgICAgICAgICAgICBJTlRFUk5BTCBNSU5UL0JVUk4gTE9HSUMKICAgIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vKi8KCiAgICBmdW5jdGlvbiBfbWludChhZGRyZXNzIHRvLCB1aW50MjU2IGFtb3VudCkgaW50ZXJuYWwgdmlydHVhbCB7CiAgICAgICAgdG90YWxTdXBwbHkgKz0gYW1vdW50OwoKICAgICAgICAvLyBOw6NvIHBvZGUgdWx0cmFwYXNzYXIgbyBsaW1pdGUgcG9ycXVlIGEgc29tYSBkZSB0b2RvcyBvcyB1c3XDoXJpb3MKICAgICAgICAvLyBOw6NvIHBvZGUgZXhjZWRlciBvIHZhbG9yIG3DoXhpbW8gZGUgdWludDI1Ni4KICAgICAgICB1bmNoZWNrZWQgewogICAgICAgICAgICBiYWxhbmNlT2ZbdG9dICs9IGFtb3VudDsKICAgICAgICB9CgogICAgICAgIGVtaXQgVHJhbnNmZXIoYWRkcmVzcygwKSwgdG8sIGFtb3VudCk7CiAgICB9CgogICAgZnVuY3Rpb24gX2J1cm4oYWRkcmVzcyBmcm9tLCB1aW50MjU2IGFtb3VudCkgaW50ZXJuYWwgdmlydHVhbCB7CiAgICAgICAgYmFsYW5jZU9mW2Zyb21dIC09IGFtb3VudDsKCiAgICAgICAgLy8gQ2Fubm90IHVuZGVyZmxvdyBiZWNhdXNlIGEgdXNlcidzIGJhbGFuY2UKICAgICAgICAvLyB3aWxsIG5ldmVyIGJlIGxhcmdlciB0aGFuIHRoZSB0b3RhbCBzdXBwbHkuCgogICAgICAgIC8vIE7Do28gw6kgcG9zc8OtdmVsIHVuZGVyZmxvdyBwb3JxdWUgbyBzYWxkbyBkbyB1c3XDoXJpbwogICAgICAgIC8vIG51bmNhIHNlcsOhIG1haW9yIGRvIHF1ZSBvIHN1cHJpbWVudG8gdG90YWwuCiAgICAgICAgdW5jaGVja2VkIHsKICAgICAgICAgICAgdG90YWxTdXBwbHkgLT0gYW1vdW50OwogICAgICAgIH0KCiAgICAgICAgZW1pdCBUcmFuc2Zlcihmcm9tLCBhZGRyZXNzKDApLCBhbW91bnQpOwogICAgfQp9Cgpjb250cmFjdCBFUkMyMFBlcm1pdCBpcyBFUkMyMCB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgICBzdHJpbmcgbWVtb3J5IF9uYW1lLAogICAgICAgIHN0cmluZyBtZW1vcnkgX3N5bWJvbCwKICAgICAgICB1aW50OCBfZGVjaW1hbHMKICAgICkgRVJDMjAoX25hbWUsIF9zeW1ib2wsIF9kZWNpbWFscykge30KCiAgICBmdW5jdGlvbiBtaW50KGFkZHJlc3MgdG8sIHVpbnQyNTYgYW1vdW50KSBwdWJsaWMgewogICAgICAgIF9taW50KHRvLCBhbW91bnQpOwogICAgfQp9=&version=soljson-v0.8.20+commit.a1b79de6.js) diff --git a/src/exemplos/aplicacoes/vaquinha.md b/src/exemplos/aplicacoes/vaquinha.md new file mode 100644 index 0000000..e307b71 --- /dev/null +++ b/src/exemplos/aplicacoes/vaquinha.md @@ -0,0 +1,149 @@ +# Vaquinha + +Token ERC20 de financiamento coletivo + +1. O usuário cria uma campanha. +2. Os usuários podem se comprometer, transferindo seu token para uma campanha. +3. Após o término da campanha, o criador da campanha pode reivindicar os fundos se o valor total prometido for maior que a meta da campanha. +4. Caso contrário, a campanha não atingiu seu objetivo, os usuários podem retirar sua promessa. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IERC20 { + function transfer(address, uint) external returns (bool); + + function transferFrom( + address, + address, + uint + ) external returns (bool); +} + +contract CrowdFund { + event Launch( + uint id, + address indexed creator, + uint goal, + uint32 startAt, + uint32 endAt + ); + event Cancel(uint id); + event Pledge(uint indexed id, address indexed caller, uint amount); + event Unpledge(uint indexed id, address indexed caller, uint amount); + event Claim(uint id); + event Refund(uint id, address indexed caller, uint amount); + + struct Campaign { + // Criador da campanha + address creator; + // Quantidade de tokens para arrecadar + uint goal; + // Valor total prometido + uint pledged; + // Timestamp do início da campanha + uint32 startAt; + // Timestamp do final da campanha + uint32 endAt; + // True se a meta foi alcançada e o criador reivindicou os tokens. + bool claimed; + } + + IERC20 public immutable token; + // Contagem total de campanhas criadas. + // Também é usado para gerar id para novas campanhas. + uint public count; + // Mapping do id para o Campaign + mapping(uint => Campaign) public campaigns; + // Mapping do campaign id => doador => valor prometido + mapping(uint => mapping(address => uint)) public pledgedAmount; + + constructor(address _token) { + token = IERC20(_token); + } + + function launch( + uint _goal, + uint32 _startAt, + uint32 _endAt + ) external { + require(_startAt >= block.timestamp, "start at < now"); + require(_endAt >= _startAt, "end at < start at"); + require(_endAt <= block.timestamp + 90 days, "end at > max duration"); + + count += 1; + campaigns[count] = Campaign({ + creator: msg.sender, + goal: _goal, + pledged: 0, + startAt: _startAt, + endAt: _endAt, + claimed: false + }); + + emit Launch(count, msg.sender, _goal, _startAt, _endAt); + } + + function cancel(uint _id) external { + Campaign memory campaign = campaigns[_id]; + require(campaign.creator == msg.sender, "not creator"); + require(block.timestamp < campaign.startAt, "started"); + + delete campaigns[_id]; + emit Cancel(_id); + } + + function pledge(uint _id, uint _amount) external { + Campaign storage campaign = campaigns[_id]; + require(block.timestamp >= campaign.startAt, "not started"); + require(block.timestamp <= campaign.endAt, "ended"); + + campaign.pledged += _amount; + pledgedAmount[_id][msg.sender] += _amount; + token.transferFrom(msg.sender, address(this), _amount); + + emit Pledge(_id, msg.sender, _amount); + } + + function unpledge(uint _id, uint _amount) external { + Campaign storage campaign = campaigns[_id]; + require(block.timestamp <= campaign.endAt, "ended"); + + campaign.pledged -= _amount; + pledgedAmount[_id][msg.sender] -= _amount; + token.transfer(msg.sender, _amount); + + emit Unpledge(_id, msg.sender, _amount); + } + + function claim(uint _id) external { + Campaign storage campaign = campaigns[_id]; + require(campaign.creator == msg.sender, "not creator"); + require(block.timestamp > campaign.endAt, "not ended"); + require(campaign.pledged >= campaign.goal, "pledged < goal"); + require(!campaign.claimed, "claimed"); + + campaign.claimed = true; + token.transfer(campaign.creator, campaign.pledged); + + emit Claim(_id); + } + + function refund(uint _id) external { + Campaign memory campaign = campaigns[_id]; + require(block.timestamp > campaign.endAt, "not ended"); + require(campaign.pledged < campaign.goal, "pledged >= goal"); + + uint bal = pledgedAmount[_id][msg.sender]; + pledgedAmount[_id][msg.sender] = 0; + token.transfer(msg.sender, bal); + + emit Refund(_id, msg.sender, bal); + } +} +``` + +## Teste no Remix + +- [CrowdFund.sol](https://remix.ethereum.org/#code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCmludGVyZmFjZSBJRVJDMjAgewogICAgZnVuY3Rpb24gdHJhbnNmZXIoYWRkcmVzcywgdWludCkgZXh0ZXJuYWwgcmV0dXJucyAoYm9vbCk7CgogICAgZnVuY3Rpb24gdHJhbnNmZXJGcm9tKAogICAgICAgIGFkZHJlc3MsCiAgICAgICAgYWRkcmVzcywKICAgICAgICB1aW50CiAgICApIGV4dGVybmFsIHJldHVybnMgKGJvb2wpOwp9Cgpjb250cmFjdCBDcm93ZEZ1bmQgewogICAgZXZlbnQgTGF1bmNoKAogICAgICAgIHVpbnQgaWQsCiAgICAgICAgYWRkcmVzcyBpbmRleGVkIGNyZWF0b3IsCiAgICAgICAgdWludCBnb2FsLAogICAgICAgIHVpbnQzMiBzdGFydEF0LAogICAgICAgIHVpbnQzMiBlbmRBdAogICAgKTsKICAgIGV2ZW50IENhbmNlbCh1aW50IGlkKTsKICAgIGV2ZW50IFBsZWRnZSh1aW50IGluZGV4ZWQgaWQsIGFkZHJlc3MgaW5kZXhlZCBjYWxsZXIsIHVpbnQgYW1vdW50KTsKICAgIGV2ZW50IFVucGxlZGdlKHVpbnQgaW5kZXhlZCBpZCwgYWRkcmVzcyBpbmRleGVkIGNhbGxlciwgdWludCBhbW91bnQpOwogICAgZXZlbnQgQ2xhaW0odWludCBpZCk7CiAgICBldmVudCBSZWZ1bmQodWludCBpZCwgYWRkcmVzcyBpbmRleGVkIGNhbGxlciwgdWludCBhbW91bnQpOwoKICAgIHN0cnVjdCBDYW1wYWlnbiB7CiAgICAgICAgLy8gQ3JpYWRvciBkYSBjYW1wYW5oYQogICAgICAgIGFkZHJlc3MgY3JlYXRvcjsKICAgICAgICAvLyBRdWFudGlkYWRlIGRlIHRva2VucyBwYXJhIGFycmVjYWRhcgogICAgICAgIHVpbnQgZ29hbDsKICAgICAgICAvLyBWYWxvciB0b3RhbCBwcm9tZXRpZG8KICAgICAgICB1aW50IHBsZWRnZWQ7CiAgICAgICAgLy8gVGltZXN0YW1wIGRvIGluaWNpbyBkYSBjYW1wYW5oYQogICAgICAgIHVpbnQzMiBzdGFydEF0OwogICAgICAgIC8vIFRpbWVzdGFtcCBkbyBmaW5hbCBkYSBjYW1wYW5oYQogICAgICAgIHVpbnQzMiBlbmRBdDsKICAgICAgICAvLyBUcnVlIHNlIGEgbWV0YSBmb2kgYWxjYW5jYWRhIGUgbyBjcmlhZG9yIHJlaXZpbmRpY291IG9zIHRva2Vucy4KICAgICAgICBib29sIGNsYWltZWQ7CiAgICB9CgogICAgSUVSQzIwIHB1YmxpYyBpbW11dGFibGUgdG9rZW47CiAgICAvLyBDb250YWdlbSB0b3RhbCBkZSBjYW1wYW5oYXMgY3JpYWRhcy4KICAgIC8vIFRhbWJlbSBlIHVzYWRvIHBhcmEgZ2VyYXIgaWQgcGFyYSBub3ZhcyBjYW1wYW5oYXMuCiAgICB1aW50IHB1YmxpYyBjb3VudDsKICAgIC8vIE1hcHBpbmcgZG8gaWQgcGFyYSBvIENhbXBhaWduCiAgICBtYXBwaW5nKHVpbnQgPT4gQ2FtcGFpZ24pIHB1YmxpYyBjYW1wYWlnbnM7CiAgICAvLyBNYXBwaW5nIGRvIGNhbXBhaWduIGlkID0+IGRvYWRvciA9PiB2YWxvciBwcm9tZXRpZG8KICAgIG1hcHBpbmcodWludCA9PiBtYXBwaW5nKGFkZHJlc3MgPT4gdWludCkpIHB1YmxpYyBwbGVkZ2VkQW1vdW50OwoKICAgIGNvbnN0cnVjdG9yKGFkZHJlc3MgX3Rva2VuKSB7CiAgICAgICAgdG9rZW4gPSBJRVJDMjAoX3Rva2VuKTsKICAgIH0KCiAgICBmdW5jdGlvbiBsYXVuY2goCiAgICAgICAgdWludCBfZ29hbCwKICAgICAgICB1aW50MzIgX3N0YXJ0QXQsCiAgICAgICAgdWludDMyIF9lbmRBdAogICAgKSBleHRlcm5hbCB7CiAgICAgICAgcmVxdWlyZShfc3RhcnRBdCA+PSBibG9jay50aW1lc3RhbXAsICJzdGFydCBhdCA8IG5vdyIpOwogICAgICAgIHJlcXVpcmUoX2VuZEF0ID49IF9zdGFydEF0LCAiZW5kIGF0IDwgc3RhcnQgYXQiKTsKICAgICAgICByZXF1aXJlKF9lbmRBdCA8PSBibG9jay50aW1lc3RhbXAgKyA5MCBkYXlzLCAiZW5kIGF0ID4gbWF4IGR1cmF0aW9uIik7CgogICAgICAgIGNvdW50ICs9IDE7CiAgICAgICAgY2FtcGFpZ25zW2NvdW50XSA9IENhbXBhaWduKHsKICAgICAgICAgICAgY3JlYXRvcjogbXNnLnNlbmRlciwKICAgICAgICAgICAgZ29hbDogX2dvYWwsCiAgICAgICAgICAgIHBsZWRnZWQ6IDAsCiAgICAgICAgICAgIHN0YXJ0QXQ6IF9zdGFydEF0LAogICAgICAgICAgICBlbmRBdDogX2VuZEF0LAogICAgICAgICAgICBjbGFpbWVkOiBmYWxzZQogICAgICAgIH0pOwoKICAgICAgICBlbWl0IExhdW5jaChjb3VudCwgbXNnLnNlbmRlciwgX2dvYWwsIF9zdGFydEF0LCBfZW5kQXQpOwogICAgfQoKICAgIGZ1bmN0aW9uIGNhbmNlbCh1aW50IF9pZCkgZXh0ZXJuYWwgewogICAgICAgIENhbXBhaWduIG1lbW9yeSBjYW1wYWlnbiA9IGNhbXBhaWduc1tfaWRdOwogICAgICAgIHJlcXVpcmUoY2FtcGFpZ24uY3JlYXRvciA9PSBtc2cuc2VuZGVyLCAibm90IGNyZWF0b3IiKTsKICAgICAgICByZXF1aXJlKGJsb2NrLnRpbWVzdGFtcCA8IGNhbXBhaWduLnN0YXJ0QXQsICJzdGFydGVkIik7CgogICAgICAgIGRlbGV0ZSBjYW1wYWlnbnNbX2lkXTsKICAgICAgICBlbWl0IENhbmNlbChfaWQpOwogICAgfQoKICAgIGZ1bmN0aW9uIHBsZWRnZSh1aW50IF9pZCwgdWludCBfYW1vdW50KSBleHRlcm5hbCB7CiAgICAgICAgQ2FtcGFpZ24gc3RvcmFnZSBjYW1wYWlnbiA9IGNhbXBhaWduc1tfaWRdOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wID49IGNhbXBhaWduLnN0YXJ0QXQsICJub3Qgc3RhcnRlZCIpOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wIDw9IGNhbXBhaWduLmVuZEF0LCAiZW5kZWQiKTsKCiAgICAgICAgY2FtcGFpZ24ucGxlZGdlZCArPSBfYW1vdW50OwogICAgICAgIHBsZWRnZWRBbW91bnRbX2lkXVttc2cuc2VuZGVyXSArPSBfYW1vdW50OwogICAgICAgIHRva2VuLnRyYW5zZmVyRnJvbShtc2cuc2VuZGVyLCBhZGRyZXNzKHRoaXMpLCBfYW1vdW50KTsKCiAgICAgICAgZW1pdCBQbGVkZ2UoX2lkLCBtc2cuc2VuZGVyLCBfYW1vdW50KTsKICAgIH0KCiAgICBmdW5jdGlvbiB1bnBsZWRnZSh1aW50IF9pZCwgdWludCBfYW1vdW50KSBleHRlcm5hbCB7CiAgICAgICAgQ2FtcGFpZ24gc3RvcmFnZSBjYW1wYWlnbiA9IGNhbXBhaWduc1tfaWRdOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wIDw9IGNhbXBhaWduLmVuZEF0LCAiZW5kZWQiKTsKCiAgICAgICAgY2FtcGFpZ24ucGxlZGdlZCAtPSBfYW1vdW50OwogICAgICAgIHBsZWRnZWRBbW91bnRbX2lkXVttc2cuc2VuZGVyXSAtPSBfYW1vdW50OwogICAgICAgIHRva2VuLnRyYW5zZmVyKG1zZy5zZW5kZXIsIF9hbW91bnQpOwoKICAgICAgICBlbWl0IFVucGxlZGdlKF9pZCwgbXNnLnNlbmRlciwgX2Ftb3VudCk7CiAgICB9CgogICAgZnVuY3Rpb24gY2xhaW0odWludCBfaWQpIGV4dGVybmFsIHsKICAgICAgICBDYW1wYWlnbiBzdG9yYWdlIGNhbXBhaWduID0gY2FtcGFpZ25zW19pZF07CiAgICAgICAgcmVxdWlyZShjYW1wYWlnbi5jcmVhdG9yID09IG1zZy5zZW5kZXIsICJub3QgY3JlYXRvciIpOwogICAgICAgIHJlcXVpcmUoYmxvY2sudGltZXN0YW1wID4gY2FtcGFpZ24uZW5kQXQsICJub3QgZW5kZWQiKTsKICAgICAgICByZXF1aXJlKGNhbXBhaWduLnBsZWRnZWQgPj0gY2FtcGFpZ24uZ29hbCwgInBsZWRnZWQgPCBnb2FsIik7CiAgICAgICAgcmVxdWlyZSghY2FtcGFpZ24uY2xhaW1lZCwgImNsYWltZWQiKTsKCiAgICAgICAgY2FtcGFpZ24uY2xhaW1lZCA9IHRydWU7CiAgICAgICAgdG9rZW4udHJhbnNmZXIoY2FtcGFpZ24uY3JlYXRvciwgY2FtcGFpZ24ucGxlZGdlZCk7CgogICAgICAgIGVtaXQgQ2xhaW0oX2lkKTsKICAgIH0KCiAgICBmdW5jdGlvbiByZWZ1bmQodWludCBfaWQpIGV4dGVybmFsIHsKICAgICAgICBDYW1wYWlnbiBtZW1vcnkgY2FtcGFpZ24gPSBjYW1wYWlnbnNbX2lkXTsKICAgICAgICByZXF1aXJlKGJsb2NrLnRpbWVzdGFtcCA+IGNhbXBhaWduLmVuZEF0LCAibm90IGVuZGVkIik7CiAgICAgICAgcmVxdWlyZShjYW1wYWlnbi5wbGVkZ2VkIDwgY2FtcGFpZ24uZ29hbCwgInBsZWRnZWQgPj0gZ29hbCIpOwoKICAgICAgICB1aW50IGJhbCA9IHBsZWRnZWRBbW91bnRbX2lkXVttc2cuc2VuZGVyXTsKICAgICAgICBwbGVkZ2VkQW1vdW50W19pZF1bbXNnLnNlbmRlcl0gPSAwOwogICAgICAgIHRva2VuLnRyYW5zZmVyKG1zZy5zZW5kZXIsIGJhbCk7CgogICAgICAgIGVtaXQgUmVmdW5kKF9pZCwgbXNnLnNlbmRlciwgYmFsKTsKICAgIH0KfQ==&version=soljson-v0.8.20+commit.a1b79de6.js)