├── tests ├── data.tvc ├── account.boc ├── deploy_msg.boc ├── depool_acc.boc ├── depool_acc.tvc ├── account_fift.boc ├── account_fift.tvc ├── decode_fields.tvc ├── samples │ ├── test.tvc │ ├── wallet.boc │ ├── wallet.tvc │ ├── Terminal.tvc │ ├── sample1.tvc │ ├── sample2.tvc │ ├── sample3.tvc │ ├── AmountInput.tvc │ ├── NumberInput.tvc │ ├── arguments.tvc │ ├── fakeDepool.tvc │ ├── AddressInput.tvc │ ├── ConfirmInput.tvc │ ├── PipechainTest.tvc │ ├── PipechainTest_2.tvc │ ├── SafeMultisigWallet.tvc │ ├── SafeMultisigWallet_msg.boc │ ├── exp.json │ ├── giver_v2.key │ ├── fakeDepool.key │ ├── test.keys.json │ ├── giver.abi.json │ ├── test.sol │ ├── ICompleted.sol │ ├── arguments.sol │ ├── wallet.abi.json │ ├── accumulator.abi.json │ ├── test.abi.json │ ├── giver_v2.abi.json │ ├── 1_Accumulator.sol │ ├── wallet.sol │ ├── arguments.abi.json │ ├── Terminal.sol │ ├── ConfirmInput.sol │ ├── NumberInput.sol │ ├── Subscription.abi.json │ ├── AmountInput.sol │ ├── AddressInput.sol │ ├── sample1.abi.json │ ├── sample2.abi.json │ ├── sample1.sol │ ├── sample2.sol │ ├── Terminal.abi.json │ ├── ConfirmInput.abi.json │ ├── NumberInput.abi.json │ ├── AmountInput.abi.json │ ├── AddressInput.abi.json │ ├── Debot.sol │ ├── PipechainTest.abi.json │ ├── PipechainTest_2.abi.json │ ├── SafeMultisigWallet.abi.json │ ├── sample3.abi.json │ ├── fakeDepool.abi.json │ ├── sample3.sol │ ├── test.code │ ├── fakeDepool.sol │ ├── SetcodeMultisigWallet.abi.json │ ├── PipechainTest.sol │ ├── PipechainTest_2.sol │ └── arguments.code ├── config_contract.saved ├── conf1.json ├── conf2.json ├── test.args ├── deploy_msg.base64 ├── PipechainTest2.chain ├── data.sol ├── decode_body.abi.json ├── data.abi.json ├── multisig.man2.json ├── test_abi_v2.1.abi.json ├── rfld.conf.json ├── multisig.man.json ├── test.conf.json ├── _network_test.rs ├── PipechainTest1.chain ├── depool.abi.json └── common │ └── mod.rs ├── archive.sh ├── keys.json ├── .gitignore ├── src ├── debot │ ├── interfaces │ │ ├── mod.rs │ │ ├── stdout.rs │ │ ├── echo.rs │ │ ├── confirm_input.rs │ │ ├── number_input.rs │ │ ├── address_input.rs │ │ ├── input_interface.rs │ │ ├── signing_box_input.rs │ │ ├── amount_input.rs │ │ ├── menu.rs │ │ ├── userinfo.rs │ │ ├── terminal.rs │ │ └── dinterface.rs │ ├── pipechain.rs │ ├── term_encryption_box.rs │ ├── processor.rs │ ├── term_signing_box.rs │ └── mod.rs ├── sendfile.rs ├── convert.rs ├── voting.rs ├── completion.rs └── deploy.rs ├── Cargo.toml └── Debug.md /tests/data.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/data.tvc -------------------------------------------------------------------------------- /tests/account.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/account.boc -------------------------------------------------------------------------------- /tests/deploy_msg.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/deploy_msg.boc -------------------------------------------------------------------------------- /tests/depool_acc.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/depool_acc.boc -------------------------------------------------------------------------------- /tests/depool_acc.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/depool_acc.tvc -------------------------------------------------------------------------------- /archive.sh: -------------------------------------------------------------------------------- 1 | cp target/release/ever-cli . 2 | tar -cvzf ever-cli_v$1_linux.tar.gz ever-cli 3 | rm ever-cli -------------------------------------------------------------------------------- /tests/account_fift.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/account_fift.boc -------------------------------------------------------------------------------- /tests/account_fift.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/account_fift.tvc -------------------------------------------------------------------------------- /tests/decode_fields.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/decode_fields.tvc -------------------------------------------------------------------------------- /tests/samples/test.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/test.tvc -------------------------------------------------------------------------------- /tests/samples/wallet.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/wallet.boc -------------------------------------------------------------------------------- /tests/samples/wallet.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/wallet.tvc -------------------------------------------------------------------------------- /tests/samples/Terminal.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/Terminal.tvc -------------------------------------------------------------------------------- /tests/samples/sample1.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/sample1.tvc -------------------------------------------------------------------------------- /tests/samples/sample2.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/sample2.tvc -------------------------------------------------------------------------------- /tests/samples/sample3.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/sample3.tvc -------------------------------------------------------------------------------- /tests/config_contract.saved: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/config_contract.saved -------------------------------------------------------------------------------- /tests/samples/AmountInput.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/AmountInput.tvc -------------------------------------------------------------------------------- /tests/samples/NumberInput.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/NumberInput.tvc -------------------------------------------------------------------------------- /tests/samples/arguments.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/arguments.tvc -------------------------------------------------------------------------------- /tests/samples/fakeDepool.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/fakeDepool.tvc -------------------------------------------------------------------------------- /tests/samples/AddressInput.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/AddressInput.tvc -------------------------------------------------------------------------------- /tests/samples/ConfirmInput.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/ConfirmInput.tvc -------------------------------------------------------------------------------- /tests/samples/PipechainTest.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/PipechainTest.tvc -------------------------------------------------------------------------------- /tests/samples/PipechainTest_2.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/PipechainTest_2.tvc -------------------------------------------------------------------------------- /tests/conf1.json: -------------------------------------------------------------------------------- 1 | {"url":"https://net.ton.dev","wc":0,"addr":null,"abi_path":null,"keys_path":null,"retries":10,"timeout":25000} -------------------------------------------------------------------------------- /tests/samples/SafeMultisigWallet.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/SafeMultisigWallet.tvc -------------------------------------------------------------------------------- /tests/conf2.json: -------------------------------------------------------------------------------- 1 | {"url":"https://net.ton.dev","wc":0,"addr":null,"abi_path":null,"keys_path":null,"retries":10,"timeout":25000} 2 | -------------------------------------------------------------------------------- /tests/samples/SafeMultisigWallet_msg.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/ever-cli/HEAD/tests/samples/SafeMultisigWallet_msg.boc -------------------------------------------------------------------------------- /tests/test.args: -------------------------------------------------------------------------------- 1 | { 2 | "dest": "0:ece57bcc6c530283becbbd8a3b24d3c5987cdddc3c8b7b33be6e4a6312490415", 3 | "value": 1000000000, 4 | "bounce": "false" 5 | } -------------------------------------------------------------------------------- /tests/samples/exp.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": "99af508d097fa0ce97cd533a4f964ca2e21f4fb03c3f3be01d3524b7ccc09f8d", 3 | "secret": "cad7c9a658982f93ea5d6f59caf8dece9fa925738a378ce6dfdbaf477e5bb929" 4 | } -------------------------------------------------------------------------------- /tests/samples/giver_v2.key: -------------------------------------------------------------------------------- 1 | { 2 | "public": "2ada2e65ab8eeab09490e3521415f45b6e42df9c760a639bcf53957550b25a16", 3 | "secret": "172af540e43a524763dd53b26a066d472a97c4de37d5498170564510608250c3" 4 | } -------------------------------------------------------------------------------- /tests/samples/fakeDepool.key: -------------------------------------------------------------------------------- 1 | { 2 | "public": "c8bd66f90d61f7e1e1a6151a0dbe9d8640666920d8c0cf399cbfb72e089d2e41", 3 | "secret": "ba0787badcca7d0785e9b3ba228a179fce89d8d597de3fa5f7ad5185403924bc" 4 | } -------------------------------------------------------------------------------- /tests/samples/test.keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": "c8bd66f90d61f7e1e1a6151a0dbe9d8640666920d8c0cf399cbfb72e089d2e41", 3 | "secret": "ba0787badcca7d0785e9b3ba228a179fce89d8d597de3fa5f7ad5185403924bc" 4 | } -------------------------------------------------------------------------------- /keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": "9711a04f0b19474272bc7bae5472a8fbbb6ef71ce9c193f5ec3f5af808069a41", 3 | "secret": "cdf2a820517fa783b9b6094d15e650af92d485084ab217fc2c859f02d49623f3" 4 | } -------------------------------------------------------------------------------- /tests/deploy_msg.base64: -------------------------------------------------------------------------------- 1 | te6ccgEBAgEAmQABRYgB/rmZKcHzzi5BRDu5n6pAqFnu7N2hpndIXAJYJ6T0vxYMAQDh0UERCirTGCONy01m6S+RgGVDIUsyXy8cm2zA7b0wCSQVCbMZLF7CBKn6IcPHFMSOobuEBwsCEG+k/cSz70qgA4AAAMGLxyvCMYw7TBL32QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMsA= -------------------------------------------------------------------------------- /tests/PipechainTest2.chain: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "debotAddress": "", 4 | "autoApprove": ["ApproveOnChainCall"], 5 | "quiet": true, 6 | "chain": [ 7 | { 8 | "type": "SigningBox", 9 | "handle": 0 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/samples/giver.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 1, 3 | "functions": [ 4 | { 5 | "name": "constructor", 6 | "inputs": [], 7 | "outputs": [] 8 | }, 9 | { 10 | "name": "sendGrams", 11 | "inputs": [ 12 | {"name": "dest", "type": "address"}, 13 | {"name": "amount", "type": "uint64"} 14 | ], 15 | "outputs": [] 16 | } 17 | ], 18 | "events": [], 19 | "data": [] 20 | } 21 | -------------------------------------------------------------------------------- /tests/samples/test.sol: -------------------------------------------------------------------------------- 1 | pragma ever-solidity >= 0.64.0; 2 | pragma AbiHeader expire; 3 | 4 | contract Test { 5 | 6 | modifier checkOwnerAndAccept { 7 | require(msg.pubkey() == tvm.pubkey(), 102); 8 | tvm.accept(); 9 | _; 10 | } 11 | 12 | function test(uint ctype, string data) public checkOwnerAndAccept { 13 | } 14 | 15 | function get(uint ctype, string data) public returns (uint) { 16 | return ctype + 1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .vscode 3 | .solc_imports 4 | /everx-labs-cli.conf.json 5 | /ever-cli.conf.json 6 | /call.boc 7 | /key1 8 | /tests/data.tvc 9 | /tests/samples/wallet.keys.json 10 | /tests/samples/tmp.json 11 | /tests/sample1.keys.json 12 | 13 | tests/deploy_test.key 14 | .idea/* 15 | 16 | debot_err.log 17 | *.keys.json 18 | 19 | tests/PipechainTest1.chain 20 | *.txns 21 | *.log 22 | *.boc 23 | !tests/samples/SafeMultisigWallet_msg.boc -------------------------------------------------------------------------------- /tests/samples/ICompleted.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.47.0; 2 | 3 | enum Status { 4 | Passed, 5 | Failed 6 | } 7 | 8 | struct Data { 9 | bytes data; 10 | } 11 | 12 | interface IOnInvokeCompleted { 13 | function OnInvokeCompleted(Status status, mapping(uint32 => Data) ret1) external; 14 | } 15 | 16 | contract Test is IOnInvokeCompleted { 17 | function OnInvokeCompleted(Status status, mapping(uint32 => Data) ret1) external override {} 18 | } -------------------------------------------------------------------------------- /tests/samples/arguments.sol: -------------------------------------------------------------------------------- 1 | pragma ever-solidity >=0.63.0; 2 | pragma AbiHeader expire; 3 | 4 | contract Wallet { 5 | uint public val; 6 | 7 | function add(uint addr, uint keys, uint abi, uint method) public { 8 | tvm.accept(); 9 | val += addr + keys + abi + method; 10 | } 11 | 12 | function get(uint addr, uint keys, uint abi, uint method) public view returns (uint) { 13 | return val + addr + keys + abi + method; 14 | } 15 | } -------------------------------------------------------------------------------- /tests/samples/wallet.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "sendTransaction", 7 | "inputs": [ 8 | {"name":"dest","type":"address"}, 9 | {"name":"value","type":"uint128"}, 10 | {"name":"bounce","type":"bool"} 11 | ], 12 | "outputs": [ 13 | ] 14 | }, 15 | { 16 | "name": "fallback", 17 | "inputs": [ 18 | ], 19 | "outputs": [ 20 | ] 21 | }, 22 | { 23 | "name": "constructor", 24 | "inputs": [ 25 | ], 26 | "outputs": [ 27 | ] 28 | } 29 | ], 30 | "events": [ 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /tests/data.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.0; 2 | pragma AbiHeader v1; 3 | 4 | contract TestData { 5 | 6 | uint256 public m_id; 7 | 8 | /* 9 | * Publics 10 | */ 11 | 12 | function sendTransaction(address dest, uint128 value, bool bounce) public view { 13 | require(msg.pubkey() == tvm.pubkey(), 101); 14 | tvm.accept(); 15 | tvm.transfer(dest, value, bounce, 3); 16 | } 17 | 18 | function getId() public view returns (uint256 id) { 19 | id = m_id; 20 | } 21 | 22 | function getKey() public view returns (uint256 key) { 23 | key = tvm.pubkey(); 24 | } 25 | } -------------------------------------------------------------------------------- /tests/samples/accumulator.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.3", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "constructor", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "add", 15 | "inputs": [ 16 | {"name":"value","type":"uint256"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "sum", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"sum","type":"uint256"} 27 | ] 28 | } 29 | ], 30 | "data": [ 31 | ], 32 | "events": [ 33 | ], 34 | "fields": [ 35 | {"name":"_pubkey","type":"uint256"}, 36 | {"name":"_timestamp","type":"uint64"}, 37 | {"name":"_constructorFlag","type":"bool"}, 38 | {"name":"sum","type":"uint256"} 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /tests/decode_body.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "constructor", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "add", 15 | "inputs": [ 16 | {"name":"value","type":"uint256"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "sum", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"sum","type":"uint256"} 27 | ] 28 | } 29 | ], 30 | "data": [ 31 | ], 32 | "events": [ 33 | ], 34 | "fields": [ 35 | {"name":"_pubkey","type":"uint256"}, 36 | {"name":"_timestamp","type":"uint64"}, 37 | {"name":"_constructorFlag","type":"bool"}, 38 | {"name":"sum","type":"uint256"} 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /tests/data.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 1, 3 | "functions": [ 4 | { 5 | "name": "sendTransaction", 6 | "inputs": [ 7 | {"name":"dest","type":"address"}, 8 | {"name":"value","type":"uint128"}, 9 | {"name":"bounce","type":"bool"} 10 | ], 11 | "outputs": [ 12 | ] 13 | }, 14 | { 15 | "name": "getId", 16 | "inputs": [ 17 | ], 18 | "outputs": [ 19 | {"name":"id","type":"uint256"} 20 | ] 21 | }, 22 | { 23 | "name": "getKey", 24 | "inputs": [ 25 | ], 26 | "outputs": [ 27 | {"name":"key","type":"uint256"} 28 | ] 29 | }, 30 | { 31 | "name": "constructor", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | ] 36 | } 37 | ], 38 | "data": [ 39 | {"key":1,"name":"m_id","type":"uint256"} 40 | ], 41 | "events": [ 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /tests/samples/test.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.3", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "test", 8 | "inputs": [ 9 | {"name":"ctype","type":"uint256"}, 10 | {"name":"data","type":"string"} 11 | ], 12 | "outputs": [ 13 | ] 14 | }, 15 | { 16 | "name": "get", 17 | "inputs": [ 18 | {"name":"ctype","type":"uint256"}, 19 | {"name":"data","type":"string"} 20 | ], 21 | "outputs": [ 22 | {"name":"value0","type":"uint256"} 23 | ] 24 | }, 25 | { 26 | "name": "constructor", 27 | "inputs": [ 28 | ], 29 | "outputs": [ 30 | ] 31 | } 32 | ], 33 | "data": [ 34 | ], 35 | "events": [ 36 | ], 37 | "fields": [ 38 | {"name":"_pubkey","type":"uint256"}, 39 | {"name":"_timestamp","type":"uint64"}, 40 | {"name":"_constructorFlag","type":"bool"} 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /tests/multisig.man2.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "debotAddress": "0:09403116d2d04f3d86ab2de138b390f6ec1b0bc02363dbf006953946e807051e", 4 | "initMethod": "invokeTransaction", 5 | "initArgs": { 6 | "sender": "0:66e01d6df5a8d7677d9ab2daf7f258f1e2a7fe73da5320300395f99e01dc3b5f", 7 | "recipient": "0:841288ed3b55d9cdafa806807f02a0ae0c169aa5edfe88a789a6482429756a94", 8 | "amount": "1500000000", 9 | "bounce": true, 10 | "payload": "" 11 | }, 12 | "quiet": false, 13 | "autoApprove": ["ApproveOnChainCall"], 14 | "chain": [ 15 | { 16 | "type": "Input", 17 | "interface": "16653eaf34c921467120f2685d425ff963db5cbb5aa676a62a2e33bfc3f6828a", 18 | "method": "get", 19 | "params": { "value": true } 20 | }, 21 | { 22 | "type": "SigningBox", 23 | "handle": 0 24 | }] 25 | } 26 | -------------------------------------------------------------------------------- /tests/samples/giver_v2.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "upgrade", 7 | "inputs": [ 8 | {"name":"newcode","type":"cell"} 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "sendTransaction", 15 | "inputs": [ 16 | {"name":"dest","type":"address"}, 17 | {"name":"value","type":"uint128"}, 18 | {"name":"bounce","type":"bool"} 19 | ], 20 | "outputs": [ 21 | ] 22 | }, 23 | { 24 | "name": "getMessages", 25 | "inputs": [ 26 | ], 27 | "outputs": [ 28 | {"components":[{"name":"hash","type":"uint256"},{"name":"expireAt","type":"uint64"}],"name":"messages","type":"tuple[]"} 29 | ] 30 | }, 31 | { 32 | "name": "constructor", 33 | "inputs": [ 34 | ], 35 | "outputs": [ 36 | ] 37 | } 38 | ], 39 | "events": [ 40 | ] 41 | } -------------------------------------------------------------------------------- /tests/test_abi_v2.1.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.1", 4 | "header": ["time"], 5 | "functions": [ 6 | { 7 | "name": "constructor", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "f", 15 | "inputs": [ 16 | {"name":"cells","type":"cell[]"} 17 | ], 18 | "outputs": [ 19 | ] 20 | } 21 | ], 22 | "data": [ 23 | ], 24 | "events": [ 25 | ], 26 | "fields": [ 27 | {"name":"__pubkey","type":"uint256"}, 28 | {"name":"__timestamp","type":"uint64"}, 29 | {"name":"fun","type":"uint32"}, 30 | {"name":"opt","type":"optional(bytes)"}, 31 | {"components":[{"name":"value0","type":"uint256"},{"name":"value1","type":"uint256"},{"name":"value2","type":"uint256"},{"name":"value3","type":"uint256"}],"name":"big","type":"optional(tuple)"}, 32 | {"name":"a","type":"string"}, 33 | {"name":"b","type":"bytes"}, 34 | {"name":"length","type":"uint256"} 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tests/samples/1_Accumulator.sol: -------------------------------------------------------------------------------- 1 | pragma ever-solidity >= 0.35.0; 2 | pragma AbiHeader expire; 3 | 4 | contract Accumulator { 5 | 6 | // State variable storing the sum of arguments that were passed to function 'add', 7 | uint public sum = 0; 8 | 9 | constructor() public { 10 | // check that contract's public key is set 11 | require(tvm.pubkey() != 0, 101); 12 | // Check that message has signature (msg.pubkey() is not zero) and message is signed with the owner's private key 13 | require(msg.pubkey() == tvm.pubkey(), 102); 14 | tvm.accept(); 15 | } 16 | 17 | // Modifier that allows to accept some external messages 18 | modifier checkOwnerAndAccept { 19 | // Check that message was signed with contracts key. 20 | require(msg.pubkey() == tvm.pubkey(), 102); 21 | tvm.accept(); 22 | _; 23 | } 24 | 25 | // Function that adds its argument to the state variable. 26 | function add(uint value) public checkOwnerAndAccept { 27 | sum += value; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/samples/wallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.0; 2 | pragma AbiHeader expire; 3 | 4 | /// @title Simple wallet 5 | /// @author EverX 6 | contract Wallet { 7 | /* 8 | * Storage 9 | */ 10 | 11 | /* 12 | Exception codes: 13 | 100 - message sender is not a wallet owner. 14 | */ 15 | 16 | modifier checkOwnerAndAccept virtual { 17 | require(msg.pubkey() == tvm.pubkey(), 100); 18 | tvm.accept(); 19 | _; 20 | } 21 | 22 | /* 23 | * Public functions 24 | */ 25 | 26 | /// @dev Allows to transfer grams to destination account. 27 | /// @param dest Transfer target address. 28 | /// @param value Nanograms value to transfer. 29 | /// @param bounce Flag that enables bounce message in case of target contract error. 30 | function sendTransaction(address dest, uint128 value, bool bounce) public checkOwnerAndAccept virtual { 31 | tvm.transfer(dest, value, bounce, 3); 32 | } 33 | 34 | fallback () external payable virtual {} 35 | } -------------------------------------------------------------------------------- /tests/samples/arguments.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "add", 8 | "inputs": [ 9 | {"name":"addr","type":"uint256"}, 10 | {"name":"keys","type":"uint256"}, 11 | {"name":"abi","type":"uint256"}, 12 | {"name":"method","type":"uint256"} 13 | ], 14 | "outputs": [ 15 | ] 16 | }, 17 | { 18 | "name": "get", 19 | "inputs": [ 20 | {"name":"addr","type":"uint256"}, 21 | {"name":"keys","type":"uint256"}, 22 | {"name":"abi","type":"uint256"}, 23 | {"name":"method","type":"uint256"} 24 | ], 25 | "outputs": [ 26 | {"name":"value0","type":"uint256"} 27 | ] 28 | }, 29 | { 30 | "name": "constructor", 31 | "inputs": [ 32 | ], 33 | "outputs": [ 34 | ] 35 | }, 36 | { 37 | "name": "val", 38 | "inputs": [ 39 | ], 40 | "outputs": [ 41 | {"name":"val","type":"uint256"} 42 | ] 43 | } 44 | ], 45 | "data": [ 46 | ], 47 | "events": [ 48 | ], 49 | "fields": [ 50 | {"name":"_pubkey","type":"uint256"}, 51 | {"name":"_timestamp","type":"uint64"}, 52 | {"name":"_constructorFlag","type":"bool"}, 53 | {"name":"val","type":"uint256"} 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /src/debot/interfaces/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2021 EverX Labs Ltd. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | pub mod address_input; 15 | pub mod amount_input; 16 | pub mod confirm_input; 17 | pub mod dinterface; 18 | pub mod echo; 19 | pub mod encryption_box_input; 20 | pub mod input_interface; 21 | pub mod menu; 22 | pub mod number_input; 23 | pub mod signing_box_input; 24 | pub mod stdout; 25 | pub mod terminal; 26 | pub mod userinfo; 27 | pub use address_input::AddressInput; 28 | pub use amount_input::AmountInput; 29 | pub use confirm_input::ConfirmInput; 30 | pub use encryption_box_input::EncryptionBoxInput; 31 | pub use input_interface::InputInterface; 32 | pub use menu::Menu; 33 | pub use number_input::NumberInput; 34 | pub use signing_box_input::SigningBoxInput; 35 | pub use terminal::Terminal; 36 | pub use userinfo::UserInfo; 37 | -------------------------------------------------------------------------------- /tests/rfld.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "url": "rfld-dapp.itgold.io", 4 | "wc": 0, 5 | "addr": "0:008928cb91e6df28d691b5dee7ad5417dc7f525adf6c022ad36cc338e9e6e861", 6 | "method": "sendTo", 7 | "parameters": null, 8 | "wallet": null, 9 | "pubkey": null, 10 | "abi_path": "/home/boris/tonix/opt/Novi/build/Novi.abi.json", 11 | "keys_path": null, 12 | "retries": 5, 13 | "timeout": 40000, 14 | "message_processing_timeout": 40000, 15 | "out_of_sync_threshold": 15, 16 | "is_json": true, 17 | "depool_fee": 0.5, 18 | "lifetime": 60, 19 | "no_answer": true, 20 | "balance_in_tons": true, 21 | "local_run": false, 22 | "async_call": false, 23 | "debug_fail": "None", 24 | "endpoints": [] 25 | }, 26 | "endpoints_map": { 27 | "http://127.0.0.1/": [ 28 | "http://0.0.0.0/", 29 | "http://127.0.0.1/", 30 | "http://localhost/" 31 | ], 32 | "main.ton.dev": [ 33 | "https://eri01.main.everos.dev", 34 | "https://gra01.main.everos.dev", 35 | "https://gra02.main.everos.dev", 36 | "https://lim01.main.everos.dev", 37 | "https://rbx01.main.everos.dev" 38 | ], 39 | "net.ton.dev": [ 40 | "https://eri01.net.everos.dev", 41 | "https://rbx01.net.everos.dev", 42 | "https://gra01.net.everos.dev" 43 | ] 44 | } 45 | } -------------------------------------------------------------------------------- /tests/samples/Terminal.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.43.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "Debot.sol"; 8 | 9 | contract TerminalDebot is Debot { 10 | function start() public override { 11 | Terminal.input(tvm.functionId(setText), "Enter text", false); 12 | } 13 | 14 | function setText(string value) public { 15 | if (value == "Test value") { 16 | Terminal.print(0, "Terminal tests completed!"); 17 | } 18 | } 19 | 20 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 21 | return [ Terminal.ID]; 22 | } 23 | 24 | function getDebotInfo() public functionID(0xDEB) override view returns( 25 | string name, string version, string publisher, string caption, string author, 26 | address support, string hello, string language, string dabi, bytes icon 27 | ) { 28 | name = "Terminal DeBot"; 29 | version = "0.1.0"; 30 | publisher = "EverX"; 31 | caption = "How to use the Terminal interface"; 32 | author = "EverX"; 33 | support = address(0); 34 | hello = "Hello, i am a Terminal example DeBot."; 35 | language = "en"; 36 | dabi = m_debotAbi.get(); 37 | icon = ""; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/sendfile.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2021 EverX Labs Ltd. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | use crate::call::send_message_and_wait; 14 | use crate::config::Config; 15 | use crate::helpers::create_client_verbose; 16 | 17 | pub async fn sendfile(config: &Config, msg_boc: &str) -> Result<(), String> { 18 | let ton = create_client_verbose(config)?; 19 | let boc_vec = std::fs::read(msg_boc).map_err(|e| format!("failed to read boc file: {}", e))?; 20 | let tvm_msg = ever_sdk::Contract::deserialize_message(&boc_vec[..]) 21 | .map_err(|e| format!("failed to parse message from boc: {}", e))?; 22 | let dst = tvm_msg 23 | .dst() 24 | .ok_or("failed to parse dst address".to_string())?; 25 | 26 | if !config.is_json { 27 | println!("Sending message to account {}", dst); 28 | } 29 | send_message_and_wait(ton, None, base64::encode(&boc_vec), config).await?; 30 | if !config.is_json { 31 | println!("Succeded."); 32 | } 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /tests/multisig.man.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "debotAddress": "0:09403116d2d04f3d86ab2de138b390f6ec1b0bc02363dbf006953946e807051e", 4 | "quiet": true, 5 | "autoApprove": ["ApproveOnChainCall"], 6 | "chain": [ 7 | { 8 | "type": "Input", 9 | "interface": "d7ed1bd8e6230871116f4522e58df0a93c5520c56f4ade23ef3d8919a984653b", 10 | "method": "get", 11 | "params": { "value": "0:66e01d6df5a8d7677d9ab2daf7f258f1e2a7fe73da5320300395f99e01dc3b5f" } 12 | }, 13 | { 14 | "type":"Input", 15 | "interface": "ac1a4d3ecea232e49783df4a23a81823cdca3205dc58cd20c4db259c25605b48", 16 | "method": "select", 17 | "params": { "index": 0 } 18 | }, 19 | { 20 | "type": "Input", 21 | "interface": "d7ed1bd8e6230871116f4522e58df0a93c5520c56f4ade23ef3d8919a984653b", 22 | "method": "get", 23 | "params": { "value": "0:841288ed3b55d9cdafa806807f02a0ae0c169aa5edfe88a789a6482429756a94"} 24 | }, 25 | { 26 | "type":"Input", 27 | "interface": "a1d347099e29c1624c8890619daf207bde18e92df5220a54bcc6d858309ece84", 28 | "method": "get", 29 | "params": { "value": "1500000000" } 30 | }, 31 | { 32 | "type": "Input", 33 | "interface": "16653eaf34c921467120f2685d425ff963db5cbb5aa676a62a2e33bfc3f6828a", 34 | "method": "get", 35 | "params": { "value": true } 36 | }, 37 | { 38 | "type": "SigningBox", 39 | "handle": 0 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /tests/test.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "url": "http://127.0.0.1/", 4 | "wc": 0, 5 | "addr": "0:2bb4a0e8391e7ea8877f4825064924bd41ce110fce97e939d3323999e1efbb13", 6 | "method": null, 7 | "parameters": null, 8 | "wallet": "0:0000000000000000000000000000000000000000000000000000000000000001", 9 | "pubkey": "0x0000000000000000000000000000000000000000000000000000000000000002", 10 | "abi_path": null, 11 | "keys_path": null, 12 | "retries": 5, 13 | "timeout": 40000, 14 | "message_processing_timeout": 40000, 15 | "out_of_sync_threshold": 15, 16 | "is_json": false, 17 | "depool_fee": 0.5, 18 | "lifetime": 60, 19 | "no_answer": true, 20 | "balance_in_tons": false, 21 | "local_run": false, 22 | "async_call": false, 23 | "debug_fail": "None", 24 | "endpoints": [ 25 | "http://0.0.0.0/", 26 | "http://127.0.0.1/", 27 | "http://localhost/" 28 | ] 29 | }, 30 | "endpoints_map": { 31 | "http://127.0.0.1/": [ 32 | "http://0.0.0.0/", 33 | "http://127.0.0.1/", 34 | "http://localhost/" 35 | ], 36 | "main.ton.dev": [ 37 | "https://eri01.main.everos.dev", 38 | "https://gra01.main.everos.dev", 39 | "https://gra02.main.everos.dev", 40 | "https://lim01.main.everos.dev", 41 | "https://rbx01.main.everos.dev" 42 | ], 43 | "net.ton.dev": [ 44 | "https://eri01.net.everos.dev", 45 | "https://rbx01.net.everos.dev", 46 | "https://gra01.net.everos.dev" 47 | ] 48 | } 49 | } -------------------------------------------------------------------------------- /tests/samples/ConfirmInput.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.35.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "Debot.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/ConfirmInput/ConfirmInput.sol"; 8 | 9 | contract ConfirmInputDebot is Debot { 10 | function start() public override { 11 | ConfirmInput.get(tvm.functionId(setResult), "Select:"); 12 | } 13 | 14 | function setResult(bool value) public { 15 | if (value) { 16 | Terminal.print(0, "ConfirmInput tests completed!"); 17 | } 18 | } 19 | 20 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 21 | return [ Terminal.ID, ConfirmInput.ID ]; 22 | } 23 | 24 | function getDebotInfo() public functionID(0xDEB) override view returns( 25 | string name, string version, string publisher, string caption, string author, 26 | address support, string hello, string language, string dabi, bytes icon 27 | ) { 28 | name = "ConfirmInput example DeBot"; 29 | version = "0.1.0"; 30 | publisher = "EverX"; 31 | caption = "How to use the ConfirmInput interface"; 32 | author = "EverX"; 33 | support = address(0); 34 | hello = "Hello, i am an ConfirmInput example DeBot."; 35 | language = "en"; 36 | dabi = m_debotAbi.get(); 37 | icon = ""; 38 | } 39 | } -------------------------------------------------------------------------------- /tests/samples/NumberInput.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.35.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "Debot.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/NumberInput/NumberInput.sol"; 8 | 9 | contract NumberInputDebot is Debot { 10 | function start() public override { 11 | NumberInput.get(tvm.functionId(setNumber), "Enter number:", 0, 100); 12 | } 13 | 14 | function setNumber(int256 value) public { 15 | if (value == int256(79)) { 16 | Terminal.print(0, "NumberInput tests completed!"); 17 | } 18 | } 19 | 20 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 21 | return [ Terminal.ID, NumberInput.ID ]; 22 | } 23 | 24 | function getDebotInfo() public functionID(0xDEB) override view returns( 25 | string name, string version, string publisher, string caption, string author, 26 | address support, string hello, string language, string dabi, bytes icon 27 | ) { 28 | name = "NumberInput example DeBot"; 29 | version = "0.1.0"; 30 | publisher = "EverX"; 31 | caption = "How to use the NumberInput interface"; 32 | author = "EverX"; 33 | support = address(0); 34 | hello = "Hello, i am an NumberInput example DeBot."; 35 | language = "en"; 36 | dabi = m_debotAbi.get(); 37 | icon = ""; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/debot/interfaces/stdout.rs: -------------------------------------------------------------------------------- 1 | use ever_client::abi::Abi; 2 | use ever_client::debot::{DebotInterface, InterfaceResult}; 3 | use serde_json::{json, Value}; 4 | 5 | const STDOUT_ID: &str = "c91dcc3fddb30485a3a07eb7c1e5e2aceaf75f4bc2678111de1f25291cdda80b"; 6 | 7 | pub const STDOUT_ABI: &str = r#"{ 8 | "ABI version": 2, 9 | "header": ["time"], 10 | "functions": [ 11 | { 12 | "name": "print", 13 | "inputs": [ 14 | {"name":"message","type":"bytes"} 15 | ], 16 | "outputs": [ 17 | ] 18 | }, 19 | { 20 | "name": "constructor", 21 | "inputs": [ 22 | ], 23 | "outputs": [ 24 | ] 25 | } 26 | ], 27 | "data": [ 28 | ], 29 | "events": [ 30 | ] 31 | }"#; 32 | 33 | pub struct Stdout {} 34 | impl Stdout { 35 | pub fn new() -> Self { 36 | Self {} 37 | } 38 | pub fn print(&self, args: &Value) -> InterfaceResult { 39 | let text_vec = hex::decode(args["message"].as_str().unwrap()).unwrap(); 40 | let text = std::str::from_utf8(&text_vec).unwrap(); 41 | println!("{}", text); 42 | Ok((0, json!({}))) 43 | } 44 | } 45 | 46 | #[async_trait::async_trait] 47 | impl DebotInterface for Stdout { 48 | fn get_id(&self) -> String { 49 | STDOUT_ID.to_string() 50 | } 51 | 52 | fn get_abi(&self) -> Abi { 53 | Abi::Json(STDOUT_ABI.to_owned()) 54 | } 55 | 56 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 57 | match func { 58 | "print" => self.print(args), 59 | _ => Err(format!("function \"{}\" is not implemented", func)), 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/debot/pipechain.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_json::Value; 3 | use std::default::Default; 4 | 5 | fn default_init_method() -> String { 6 | "start".to_string() 7 | } 8 | fn default_mandatory() -> bool { 9 | false 10 | } 11 | 12 | #[allow(clippy::enum_variant_names)] 13 | #[derive(Deserialize, Clone, PartialEq)] 14 | pub enum ApproveKind { 15 | ApproveOnChainCall, 16 | ApproveNetwork, 17 | ApproveMessageLimit, 18 | } 19 | 20 | #[derive(Deserialize, Clone, Default)] 21 | #[serde(rename_all(deserialize = "camelCase"))] 22 | pub struct PipeChain { 23 | #[serde(default = "default_init_method")] 24 | pub init_method: String, 25 | pub init_args: Option, 26 | pub init_msg: Option, 27 | pub abi: Option, 28 | pub auto_approve: Option>, 29 | pub quiet: bool, 30 | pub chain: Vec, 31 | } 32 | 33 | impl PipeChain { 34 | pub fn new() -> Self { 35 | Self { 36 | init_method: default_init_method(), 37 | quiet: false, 38 | ..Self::default() 39 | } 40 | } 41 | } 42 | 43 | #[derive(Serialize, Deserialize, Clone)] 44 | #[serde(tag = "type")] 45 | pub enum ChainLink { 46 | Input { 47 | interface: String, 48 | method: String, 49 | params: Option, 50 | #[serde(default = "default_mandatory")] 51 | mandatory: bool, 52 | }, 53 | OnchainCall { 54 | approve: bool, 55 | iflq: Option, 56 | ifeq: Option, 57 | }, 58 | SigningBox { 59 | handle: u32, 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /tests/samples/Subscription.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 1, 3 | "functions": [ 4 | { 5 | "name": "constructor", 6 | "inputs": [ 7 | {"name":"wallet","type":"address"} 8 | ], 9 | "outputs": [ 10 | ] 11 | }, 12 | { 13 | "name": "getWallet", 14 | "inputs": [ 15 | ], 16 | "outputs": [ 17 | {"name":"value0","type":"address"} 18 | ] 19 | }, 20 | { 21 | "name": "getSubscription", 22 | "inputs": [ 23 | {"name":"subscriptionId","type":"uint256"} 24 | ], 25 | "outputs": [ 26 | {"components":[{"name":"pubkey","type":"uint256"},{"name":"to","type":"address"},{"name":"value","type":"uint64"},{"name":"period","type":"uint32"},{"name":"start","type":"uint32"},{"name":"status","type":"uint8"}],"name":"value0","type":"tuple"} 27 | ] 28 | }, 29 | { 30 | "name": "subscribe", 31 | "inputs": [ 32 | {"name":"subscriptionId","type":"uint256"}, 33 | {"name":"pubkey","type":"uint256"}, 34 | {"name":"to","type":"address"}, 35 | {"name":"value","type":"uint64"}, 36 | {"name":"period","type":"uint32"} 37 | ], 38 | "outputs": [ 39 | ] 40 | }, 41 | { 42 | "name": "cancel", 43 | "inputs": [ 44 | {"name":"subscriptionId","type":"uint256"} 45 | ], 46 | "outputs": [ 47 | ] 48 | }, 49 | { 50 | "name": "executeSubscription", 51 | "inputs": [ 52 | {"name":"subscriptionId","type":"uint256"} 53 | ], 54 | "outputs": [ 55 | ] 56 | }, 57 | { 58 | "name": "sendAllMoney", 59 | "inputs": [ 60 | {"name":"dest_addr","type":"address"} 61 | ], 62 | "outputs": [ 63 | ] 64 | } 65 | ], 66 | "events": [ 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /tests/samples/AmountInput.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.35.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "Debot.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/AmountInput/AmountInput.sol"; 8 | 9 | contract AmountInputDebot is Debot { 10 | 11 | function start() public override { 12 | AmountInput.get(tvm.functionId(setAmountWithDecimals), "Enter amount of tons with decimals:", 9, 0, 100e9); 13 | } 14 | 15 | function setAmountWithDecimals(uint128 value) public { 16 | if (value == uint128(99456654321)) { 17 | Terminal.print(0, "AmountInput tests completed!"); 18 | } 19 | } 20 | 21 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 22 | return [ Terminal.ID, AmountInput.ID ]; 23 | } 24 | 25 | function getDebotInfo() public functionID(0xDEB) override view returns( 26 | string name, string version, string publisher, string caption, string author, 27 | address support, string hello, string language, string dabi, bytes icon 28 | ) { 29 | name = "AmountInput example DeBot"; 30 | version = "0.1.0"; 31 | publisher = "EverX"; 32 | caption = "How to use the AmountInput interface"; 33 | author = "EverX"; 34 | support = address(0); 35 | hello = "Hello, i am an AmountInput example DeBot."; 36 | language = "en"; 37 | dabi = m_debotAbi.get(); 38 | icon = ""; 39 | } 40 | } -------------------------------------------------------------------------------- /tests/samples/AddressInput.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.35.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "Debot.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/AddressInput/AddressInput.sol"; 8 | 9 | contract AddressInputDebot is Debot { 10 | address m_addr = address(0xea5be6a13f20fcdfddc2c2b0d317dfeab56718249b090767e5940137b7af89f1); 11 | function start() public override { 12 | AddressInput.get(tvm.functionId(setAddress), "Enter Address:"); 13 | } 14 | 15 | function setAddress(address value) public { 16 | if (m_addr == value) { 17 | Terminal.print(0, "AddressInput tests completed!"); 18 | } 19 | } 20 | 21 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 22 | return [ Terminal.ID, AddressInput.ID ]; 23 | } 24 | 25 | function getDebotInfo() public functionID(0xDEB) override view returns( 26 | string name, string version, string publisher, string caption, string author, 27 | address support, string hello, string language, string dabi, bytes icon 28 | ) { 29 | name = "AddressInput example DeBot"; 30 | version = "0.1.0"; 31 | publisher = "EverX"; 32 | caption = "How to use the AddressInput interface"; 33 | author = "EverX"; 34 | support = address(0); 35 | hello = "Hello, i am an AddressInput example DeBot."; 36 | language = "en"; 37 | dabi = m_debotAbi.get(); 38 | icon = ""; 39 | } 40 | } -------------------------------------------------------------------------------- /src/debot/interfaces/echo.rs: -------------------------------------------------------------------------------- 1 | use ever_client::abi::Abi; 2 | use ever_client::debot::{DebotInterface, InterfaceResult}; 3 | use serde_json::{json, Value}; 4 | 5 | const ECHO_ID: &str = "f6927c0d4bdb69e1b52d27f018d156ff04152f00558042ff674f0fec32e4369d"; 6 | 7 | pub const ECHO_ABI: &str = r#" 8 | { 9 | "ABI version": 2, 10 | "header": ["time"], 11 | "functions": [ 12 | { 13 | "name": "echo", 14 | "inputs": [ 15 | {"name":"answerId","type":"uint32"}, 16 | {"name":"request","type":"bytes"} 17 | ], 18 | "outputs": [ 19 | {"name":"response","type":"bytes"} 20 | ] 21 | }, 22 | { 23 | "name": "constructor", 24 | "inputs": [ 25 | ], 26 | "outputs": [ 27 | ] 28 | } 29 | ], 30 | "data": [ 31 | ], 32 | "events": [ 33 | ] 34 | } 35 | "#; 36 | 37 | pub struct Echo {} 38 | impl Echo { 39 | pub fn new() -> Self { 40 | Self {} 41 | } 42 | 43 | fn echo(&self, args: &Value) -> InterfaceResult { 44 | let answer_id = u32::from_str_radix(args["answerId"].as_str().unwrap(), 10).unwrap(); 45 | let request_vec = hex::decode(args["request"].as_str().unwrap()).unwrap(); 46 | let request = std::str::from_utf8(&request_vec).unwrap(); 47 | Ok(( 48 | answer_id, 49 | json!({ "response": hex::encode(request.as_bytes()) }), 50 | )) 51 | } 52 | } 53 | 54 | #[async_trait::async_trait] 55 | impl DebotInterface for Echo { 56 | fn get_id(&self) -> String { 57 | ECHO_ID.to_string() 58 | } 59 | 60 | fn get_abi(&self) -> Abi { 61 | Abi::Json(ECHO_ABI.to_owned()) 62 | } 63 | 64 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 65 | match func { 66 | "echo" => self.echo(args), 67 | _ => Err(format!("function \"{}\" is not implemented", func)), 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/samples/sample1.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "start", 7 | "inputs": [ 8 | ], 9 | "outputs": [ 10 | ] 11 | }, 12 | { 13 | "name": "setSigningBoxHandle", 14 | "inputs": [ 15 | {"name":"handle","type":"uint32"} 16 | ], 17 | "outputs": [ 18 | ] 19 | }, 20 | { 21 | "name": "setSignature", 22 | "inputs": [ 23 | {"name":"signature","type":"bytes"} 24 | ], 25 | "outputs": [ 26 | ] 27 | }, 28 | { 29 | "name": "getDebotInfo", 30 | "id": "0xDEB", 31 | "inputs": [ 32 | ], 33 | "outputs": [ 34 | {"name":"name","type":"bytes"}, 35 | {"name":"version","type":"bytes"}, 36 | {"name":"publisher","type":"bytes"}, 37 | {"name":"caption","type":"bytes"}, 38 | {"name":"author","type":"bytes"}, 39 | {"name":"support","type":"address"}, 40 | {"name":"hello","type":"bytes"}, 41 | {"name":"language","type":"bytes"}, 42 | {"name":"dabi","type":"bytes"}, 43 | {"name":"icon","type":"bytes"} 44 | ] 45 | }, 46 | { 47 | "name": "getRequiredInterfaces", 48 | "inputs": [ 49 | ], 50 | "outputs": [ 51 | {"name":"interfaces","type":"uint256[]"} 52 | ] 53 | }, 54 | { 55 | "name": "getDebotOptions", 56 | "inputs": [ 57 | ], 58 | "outputs": [ 59 | {"name":"options","type":"uint8"}, 60 | {"name":"debotAbi","type":"bytes"}, 61 | {"name":"targetAbi","type":"bytes"}, 62 | {"name":"targetAddr","type":"address"} 63 | ] 64 | }, 65 | { 66 | "name": "setABI", 67 | "inputs": [ 68 | {"name":"dabi","type":"bytes"} 69 | ], 70 | "outputs": [ 71 | ] 72 | }, 73 | { 74 | "name": "constructor", 75 | "inputs": [ 76 | ], 77 | "outputs": [ 78 | ] 79 | } 80 | ], 81 | "data": [ 82 | ], 83 | "events": [ 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 'EverX Labs Ltd ' ] 3 | build = 'build.rs' 4 | default-run = 'ever-cli' 5 | description = 'command line tool for TVM blockchain' 6 | documentation = 'https://docs.everos.dev/' 7 | edition = '2021' 8 | homepage = 'https://docs.everos.dev/' 9 | keywords = [ 'TVM', 'SDK', 'smart-contract', 'everx-labs', 'solidity' ] 10 | license = 'Apache-2.0' 11 | name = 'ever-cli' 12 | readme = 'README.md' 13 | repository = 'https://github.com/everx-labs/ever-cli' 14 | version = '0.44.0' 15 | 16 | [dependencies] 17 | anyhow = '1.0' 18 | async-trait = '0.1.42' 19 | base64 = '0.13' 20 | chrono = '0.4' 21 | clap = '2.32' 22 | futures = '0.3' 23 | hex = '0.4' 24 | indicatif = '0.17' 25 | log = { features = [ 'std' ], version = '0.4' } 26 | num-bigint = '0.4' 27 | num-traits = '0.2' 28 | qr2term = '0.2' 29 | regex = '1.5' 30 | reqwest = '0.12' 31 | serde = { features = [ 'derive' ], version = '1.0' } 32 | serde_derive = '1.0' 33 | serde_json = '1.0' 34 | simplelog = '0.8' 35 | thiserror = '1.0' 36 | tokio = { default-features = false, features = [ 'full' ], version = '1.21' } 37 | tokio-retry = '0.3' 38 | url = '2.3.1' 39 | ever_abi = { git = 'https://github.com/everx-labs/ever-abi.git', tag = '2.8.1' } 40 | ever_assembler = { git = 'https://github.com/everx-labs/ever-assembler.git', tag = '1.8.0' } 41 | ever_block = { git = 'https://github.com/everx-labs/ever-block.git', tag = '1.11.20' } 42 | ever_block_json = { git = 'https://github.com/everx-labs/ever-block-json.git', tag = '0.9.37' } 43 | ever_client = { git = 'https://github.com/everx-labs/ever-sdk.git', tag = '1.49.1' } 44 | ever_executor = { git = 'https://github.com/everx-labs/ever-executor.git', tag = '1.18.22' } 45 | ever_sdk = { git = 'https://github.com/everx-labs/ever-sdk.git', tag = '1.49.1' } 46 | ever_vm = { git = 'https://github.com/everx-labs/ever-vm.git', tag = '2.2.21' } 47 | 48 | [dev-dependencies] 49 | assert_cmd = '2.0' 50 | lazy_static = '1.4' 51 | predicates = '2.1' 52 | string-error = '0.1.0' 53 | 54 | -------------------------------------------------------------------------------- /tests/_network_test.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | mod common; 3 | use common::create::{BIN_NAME, GIVER_V2_ABI, GIVER_V2_ADDR, GIVER_V2_KEY, NETWORK}; 4 | 5 | #[test] 6 | fn test_network() -> Result<(), Box> { 7 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 8 | cmd.arg("config").arg("clear"); 9 | cmd.assert().success(); 10 | 11 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 12 | cmd.arg("config").arg("endpoint").arg("reset"); 13 | cmd.assert().success(); 14 | 15 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 16 | cmd.arg("config").arg("--global").arg("clear"); 17 | cmd.assert().success(); 18 | 19 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 20 | cmd.arg("config") 21 | .arg("--global") 22 | .arg("endpoint") 23 | .arg("reset"); 24 | cmd.assert().success(); 25 | 26 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 27 | cmd.arg("config").arg("--url").arg(&*NETWORK); 28 | cmd.assert().success(); 29 | 30 | let mut cmd = Command::cargo_bin(BIN_NAME).unwrap(); 31 | let res = cmd 32 | .arg("call") 33 | .arg("--abi") 34 | .arg(GIVER_V2_ABI) 35 | .arg(GIVER_V2_ADDR) 36 | .arg("--sign") 37 | .arg(GIVER_V2_KEY) 38 | .arg("sendTransaction") 39 | .arg(format!( 40 | r#"{{"dest":"{}","value":10000000,"bounce":false}}"#, 41 | GIVER_V2_ADDR 42 | )) 43 | .assert(); 44 | let res = res.get_output().stdout.clone(); 45 | let res = String::from_utf8(res); 46 | if res.is_err() { 47 | return Err(string_error::into_err( 48 | "Failed to decode output.".to_string(), 49 | )); 50 | } 51 | 52 | if res 53 | .unwrap() 54 | .contains("Fetch first block failed: Can not send http request:") 55 | { 56 | return Err(string_error::into_err( 57 | "Node SE is not running. If it is CI run, just restart it.".to_string(), 58 | )); 59 | } 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /tests/samples/sample2.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "setParams", 7 | "inputs": [ 8 | {"name":"wallet","type":"address"}, 9 | {"name":"key","type":"uint256"} 10 | ], 11 | "outputs": [ 12 | ] 13 | }, 14 | { 15 | "name": "start", 16 | "inputs": [ 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "setWallet", 23 | "inputs": [ 24 | {"name":"value","type":"address"} 25 | ], 26 | "outputs": [ 27 | ] 28 | }, 29 | { 30 | "name": "setKey", 31 | "inputs": [ 32 | {"name":"value","type":"uint256"} 33 | ], 34 | "outputs": [ 35 | ] 36 | }, 37 | { 38 | "name": "getDebotInfo", 39 | "id": "0xDEB", 40 | "inputs": [ 41 | ], 42 | "outputs": [ 43 | {"name":"name","type":"bytes"}, 44 | {"name":"version","type":"bytes"}, 45 | {"name":"publisher","type":"bytes"}, 46 | {"name":"caption","type":"bytes"}, 47 | {"name":"author","type":"bytes"}, 48 | {"name":"support","type":"address"}, 49 | {"name":"hello","type":"bytes"}, 50 | {"name":"language","type":"bytes"}, 51 | {"name":"dabi","type":"bytes"}, 52 | {"name":"icon","type":"bytes"} 53 | ] 54 | }, 55 | { 56 | "name": "getRequiredInterfaces", 57 | "inputs": [ 58 | ], 59 | "outputs": [ 60 | {"name":"interfaces","type":"uint256[]"} 61 | ] 62 | }, 63 | { 64 | "name": "getDebotOptions", 65 | "inputs": [ 66 | ], 67 | "outputs": [ 68 | {"name":"options","type":"uint8"}, 69 | {"name":"debotAbi","type":"bytes"}, 70 | {"name":"targetAbi","type":"bytes"}, 71 | {"name":"targetAddr","type":"address"} 72 | ] 73 | }, 74 | { 75 | "name": "setABI", 76 | "inputs": [ 77 | {"name":"dabi","type":"bytes"} 78 | ], 79 | "outputs": [ 80 | ] 81 | }, 82 | { 83 | "name": "constructor", 84 | "inputs": [ 85 | ], 86 | "outputs": [ 87 | ] 88 | } 89 | ], 90 | "data": [ 91 | ], 92 | "events": [ 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /tests/samples/sample1.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.40.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/SigningBoxInput/SigningBoxInput.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "Sdk.sol"; 8 | import "Debot.sol"; 9 | 10 | contract ExampleContract is Debot { 11 | 12 | function start() public override { 13 | SigningBoxInput.get(tvm.functionId(setSigningBoxHandle), "Enter my signing keys:", [tvm.pubkey()]); 14 | } 15 | 16 | function setSigningBoxHandle(uint32 handle) public { 17 | Terminal.print(0, format("Signing Box Handle: {}", handle)); 18 | uint256 hash = sha256("test sign string"); 19 | Sdk.signHash(tvm.functionId(setSignature), handle, hash); 20 | } 21 | 22 | function setSignature(bytes signature) public { 23 | require(signature.length == 64, 200); 24 | uint256 hash = sha256("test sign string"); 25 | require(tvm.checkSign(hash, signature.toSlice(), tvm.pubkey()), 201); 26 | Terminal.print(0,"test sign hash passed"); 27 | } 28 | 29 | /// @notice Returns Metadata about DeBot. 30 | function getDebotInfo() public functionID(0xDEB) override view returns( 31 | string name, string version, string publisher, string caption, string author, 32 | address support, string hello, string language, string dabi, bytes icon 33 | ) { 34 | name = "SigningBoxInput"; 35 | version = "0.1.0"; 36 | publisher = "EverX"; 37 | caption = "Test for SigningBoxInput."; 38 | author = "EverX"; 39 | support = address(0); 40 | hello = "Test DeBot."; 41 | language = "en"; 42 | dabi = m_debotAbi.get(); 43 | icon = ""; 44 | } 45 | 46 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 47 | return [ Terminal.ID, SigningBoxInput.ID ]; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /tests/samples/sample2.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.45.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/UserInfo/UserInfo.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "Sdk.sol"; 8 | import "Debot.sol"; 9 | 10 | contract Sample2 is Debot { 11 | 12 | address m_wallet; 13 | uint256 m_key; 14 | 15 | function setParams(address wallet, uint256 key) public { 16 | tvm.accept(); 17 | m_wallet = wallet; 18 | m_key = key; 19 | } 20 | 21 | function start() public override { 22 | UserInfo.getAccount(tvm.functionId(setWallet)); 23 | UserInfo.getPublicKey(tvm.functionId(setKey)); 24 | } 25 | 26 | function setWallet(address value) public { 27 | require(value == m_wallet, 101); 28 | require(value != address(0), 104); 29 | Terminal.print(0, "Account is valid"); 30 | } 31 | 32 | function setKey(uint256 value) public { 33 | require(value == m_key, 102); 34 | require(value != 0, 103); 35 | Terminal.print(0, "Public key is valid"); 36 | } 37 | 38 | /// @notice Returns Metadata about DeBot. 39 | function getDebotInfo() public functionID(0xDEB) override view returns( 40 | string name, string version, string publisher, string caption, string author, 41 | address support, string hello, string language, string dabi, bytes icon 42 | ) { 43 | name = "UserInfo"; 44 | version = "0.1.0"; 45 | publisher = "EverX"; 46 | caption = "Test for UserInfo."; 47 | author = "EverX"; 48 | support = address(0); 49 | hello = "Test DeBot."; 50 | language = "en"; 51 | dabi = m_debotAbi.get(); 52 | icon = ""; 53 | } 54 | 55 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 56 | return [ Terminal.ID, UserInfo.ID ]; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /tests/samples/Terminal.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "start", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "setText", 15 | "inputs": [ 16 | {"name":"value","type":"string"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "getRequiredInterfaces", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"interfaces","type":"uint256[]"} 27 | ] 28 | }, 29 | { 30 | "name": "getDebotInfo", 31 | "id": "0xDEB", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | {"name":"name","type":"string"}, 36 | {"name":"version","type":"string"}, 37 | {"name":"publisher","type":"string"}, 38 | {"name":"caption","type":"string"}, 39 | {"name":"author","type":"string"}, 40 | {"name":"support","type":"address"}, 41 | {"name":"hello","type":"string"}, 42 | {"name":"language","type":"string"}, 43 | {"name":"dabi","type":"string"}, 44 | {"name":"icon","type":"bytes"} 45 | ] 46 | }, 47 | { 48 | "name": "getDebotOptions", 49 | "inputs": [ 50 | ], 51 | "outputs": [ 52 | {"name":"options","type":"uint8"}, 53 | {"name":"debotAbi","type":"string"}, 54 | {"name":"targetAbi","type":"string"}, 55 | {"name":"targetAddr","type":"address"} 56 | ] 57 | }, 58 | { 59 | "name": "setABI", 60 | "inputs": [ 61 | {"name":"dabi","type":"string"} 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "constructor", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | } 73 | ], 74 | "data": [ 75 | ], 76 | "events": [ 77 | ], 78 | "fields": [ 79 | {"name":"_pubkey","type":"uint256"}, 80 | {"name":"_timestamp","type":"uint64"}, 81 | {"name":"_constructorFlag","type":"bool"}, 82 | {"name":"m_options","type":"uint8"}, 83 | {"name":"m_debotAbi","type":"optional(string)"}, 84 | {"name":"m_targetAbi","type":"optional(string)"}, 85 | {"name":"m_target","type":"optional(address)"} 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /tests/samples/ConfirmInput.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "start", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "setResult", 15 | "inputs": [ 16 | {"name":"value","type":"bool"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "getRequiredInterfaces", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"interfaces","type":"uint256[]"} 27 | ] 28 | }, 29 | { 30 | "name": "getDebotInfo", 31 | "id": "0xDEB", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | {"name":"name","type":"string"}, 36 | {"name":"version","type":"string"}, 37 | {"name":"publisher","type":"string"}, 38 | {"name":"caption","type":"string"}, 39 | {"name":"author","type":"string"}, 40 | {"name":"support","type":"address"}, 41 | {"name":"hello","type":"string"}, 42 | {"name":"language","type":"string"}, 43 | {"name":"dabi","type":"string"}, 44 | {"name":"icon","type":"bytes"} 45 | ] 46 | }, 47 | { 48 | "name": "getDebotOptions", 49 | "inputs": [ 50 | ], 51 | "outputs": [ 52 | {"name":"options","type":"uint8"}, 53 | {"name":"debotAbi","type":"string"}, 54 | {"name":"targetAbi","type":"string"}, 55 | {"name":"targetAddr","type":"address"} 56 | ] 57 | }, 58 | { 59 | "name": "setABI", 60 | "inputs": [ 61 | {"name":"dabi","type":"string"} 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "constructor", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | } 73 | ], 74 | "data": [ 75 | ], 76 | "events": [ 77 | ], 78 | "fields": [ 79 | {"name":"_pubkey","type":"uint256"}, 80 | {"name":"_timestamp","type":"uint64"}, 81 | {"name":"_constructorFlag","type":"bool"}, 82 | {"name":"m_options","type":"uint8"}, 83 | {"name":"m_debotAbi","type":"optional(string)"}, 84 | {"name":"m_targetAbi","type":"optional(string)"}, 85 | {"name":"m_target","type":"optional(address)"} 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /tests/samples/NumberInput.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "start", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "setNumber", 15 | "inputs": [ 16 | {"name":"value","type":"int256"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "getRequiredInterfaces", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"interfaces","type":"uint256[]"} 27 | ] 28 | }, 29 | { 30 | "name": "getDebotInfo", 31 | "id": "0xDEB", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | {"name":"name","type":"string"}, 36 | {"name":"version","type":"string"}, 37 | {"name":"publisher","type":"string"}, 38 | {"name":"caption","type":"string"}, 39 | {"name":"author","type":"string"}, 40 | {"name":"support","type":"address"}, 41 | {"name":"hello","type":"string"}, 42 | {"name":"language","type":"string"}, 43 | {"name":"dabi","type":"string"}, 44 | {"name":"icon","type":"bytes"} 45 | ] 46 | }, 47 | { 48 | "name": "getDebotOptions", 49 | "inputs": [ 50 | ], 51 | "outputs": [ 52 | {"name":"options","type":"uint8"}, 53 | {"name":"debotAbi","type":"string"}, 54 | {"name":"targetAbi","type":"string"}, 55 | {"name":"targetAddr","type":"address"} 56 | ] 57 | }, 58 | { 59 | "name": "setABI", 60 | "inputs": [ 61 | {"name":"dabi","type":"string"} 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "constructor", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | } 73 | ], 74 | "data": [ 75 | ], 76 | "events": [ 77 | ], 78 | "fields": [ 79 | {"name":"_pubkey","type":"uint256"}, 80 | {"name":"_timestamp","type":"uint64"}, 81 | {"name":"_constructorFlag","type":"bool"}, 82 | {"name":"m_options","type":"uint8"}, 83 | {"name":"m_debotAbi","type":"optional(string)"}, 84 | {"name":"m_targetAbi","type":"optional(string)"}, 85 | {"name":"m_target","type":"optional(address)"} 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /tests/samples/AmountInput.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "start", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "setAmountWithDecimals", 15 | "inputs": [ 16 | {"name":"value","type":"uint128"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "getRequiredInterfaces", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"interfaces","type":"uint256[]"} 27 | ] 28 | }, 29 | { 30 | "name": "getDebotInfo", 31 | "id": "0xDEB", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | {"name":"name","type":"string"}, 36 | {"name":"version","type":"string"}, 37 | {"name":"publisher","type":"string"}, 38 | {"name":"caption","type":"string"}, 39 | {"name":"author","type":"string"}, 40 | {"name":"support","type":"address"}, 41 | {"name":"hello","type":"string"}, 42 | {"name":"language","type":"string"}, 43 | {"name":"dabi","type":"string"}, 44 | {"name":"icon","type":"bytes"} 45 | ] 46 | }, 47 | { 48 | "name": "getDebotOptions", 49 | "inputs": [ 50 | ], 51 | "outputs": [ 52 | {"name":"options","type":"uint8"}, 53 | {"name":"debotAbi","type":"string"}, 54 | {"name":"targetAbi","type":"string"}, 55 | {"name":"targetAddr","type":"address"} 56 | ] 57 | }, 58 | { 59 | "name": "setABI", 60 | "inputs": [ 61 | {"name":"dabi","type":"string"} 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "constructor", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | } 73 | ], 74 | "data": [ 75 | ], 76 | "events": [ 77 | ], 78 | "fields": [ 79 | {"name":"_pubkey","type":"uint256"}, 80 | {"name":"_timestamp","type":"uint64"}, 81 | {"name":"_constructorFlag","type":"bool"}, 82 | {"name":"m_options","type":"uint8"}, 83 | {"name":"m_debotAbi","type":"optional(string)"}, 84 | {"name":"m_targetAbi","type":"optional(string)"}, 85 | {"name":"m_target","type":"optional(address)"} 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /tests/samples/AddressInput.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "start", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "setAddress", 15 | "inputs": [ 16 | {"name":"value","type":"address"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "getRequiredInterfaces", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | {"name":"interfaces","type":"uint256[]"} 27 | ] 28 | }, 29 | { 30 | "name": "getDebotInfo", 31 | "id": "0xDEB", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | {"name":"name","type":"string"}, 36 | {"name":"version","type":"string"}, 37 | {"name":"publisher","type":"string"}, 38 | {"name":"caption","type":"string"}, 39 | {"name":"author","type":"string"}, 40 | {"name":"support","type":"address"}, 41 | {"name":"hello","type":"string"}, 42 | {"name":"language","type":"string"}, 43 | {"name":"dabi","type":"string"}, 44 | {"name":"icon","type":"bytes"} 45 | ] 46 | }, 47 | { 48 | "name": "getDebotOptions", 49 | "inputs": [ 50 | ], 51 | "outputs": [ 52 | {"name":"options","type":"uint8"}, 53 | {"name":"debotAbi","type":"string"}, 54 | {"name":"targetAbi","type":"string"}, 55 | {"name":"targetAddr","type":"address"} 56 | ] 57 | }, 58 | { 59 | "name": "setABI", 60 | "inputs": [ 61 | {"name":"dabi","type":"string"} 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "constructor", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | } 73 | ], 74 | "data": [ 75 | ], 76 | "events": [ 77 | ], 78 | "fields": [ 79 | {"name":"_pubkey","type":"uint256"}, 80 | {"name":"_timestamp","type":"uint64"}, 81 | {"name":"_constructorFlag","type":"bool"}, 82 | {"name":"m_options","type":"uint8"}, 83 | {"name":"m_debotAbi","type":"optional(string)"}, 84 | {"name":"m_targetAbi","type":"optional(string)"}, 85 | {"name":"m_target","type":"optional(address)"}, 86 | {"name":"m_addr","type":"address"} 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /src/convert.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2021 EverX Labs Ltd. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use ever_block::Sha256; 15 | 16 | pub fn convert_token(amount: &str) -> Result { 17 | convert_amount(amount, 9) 18 | } 19 | 20 | pub fn convert_amount(amount: &str, decimals: usize) -> Result { 21 | let parts: Vec<&str> = amount.split('.').collect(); 22 | if !parts.is_empty() && parts.len() <= 2 { 23 | let mut result = String::new(); 24 | result += parts[0]; 25 | if parts.len() == 2 { 26 | let fraction = format!("{:0 String { 43 | let integer = value / 1_000_000_000; 44 | let float = value - integer * 1_000_000_000; 45 | format!("{}.{:>09}", integer, float) 46 | } 47 | 48 | pub fn nodeid_from_pubkey(key: &[u8]) -> Result { 49 | if key.len() != 32 { 50 | return Err("Public key must be 32 byte long".to_owned()); 51 | } 52 | let mut hasher = Sha256::new(); 53 | // node id magic 54 | hasher.update([0xc6, 0xb4, 0x13, 0x48]); 55 | //key 56 | hasher.update(key); 57 | 58 | Ok(hex::encode(hasher.finalize())) 59 | } 60 | -------------------------------------------------------------------------------- /src/debot/interfaces/confirm_input.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{decode_answer_id, decode_prompt}; 2 | use crate::debot::term_browser::terminal_input; 3 | use ever_client::abi::Abi; 4 | use ever_client::debot::{DebotInterface, InterfaceResult}; 5 | use serde_json::{json, Value}; 6 | 7 | const ID: &str = "16653eaf34c921467120f2685d425ff963db5cbb5aa676a62a2e33bfc3f6828a"; 8 | 9 | pub const ABI: &str = r#" 10 | { 11 | "ABI version": 2, 12 | "version": "2.2", 13 | "header": ["time"], 14 | "functions": [ 15 | { 16 | "name": "get", 17 | "id": "0x43490cf2", 18 | "inputs": [ 19 | {"name":"answerId","type":"uint32"}, 20 | {"name":"prompt","type":"string"} 21 | ], 22 | "outputs": [ 23 | {"name":"value","type":"bool"} 24 | ] 25 | }, 26 | { 27 | "name": "constructor", 28 | "id": "0x68b55f3f", 29 | "inputs": [ 30 | ], 31 | "outputs": [ 32 | ] 33 | } 34 | ], 35 | "data": [ 36 | ], 37 | "events": [ 38 | ], 39 | "fields": [ 40 | {"name":"_pubkey","type":"uint256"}, 41 | {"name":"_timestamp","type":"uint64"}, 42 | {"name":"_constructorFlag","type":"bool"} 43 | ] 44 | } 45 | "#; 46 | 47 | pub struct ConfirmInput {} 48 | 49 | impl ConfirmInput { 50 | pub fn new() -> Self { 51 | Self {} 52 | } 53 | fn get(&self, args: &Value) -> InterfaceResult { 54 | let answer_id = decode_answer_id(args)?; 55 | let prompt = decode_prompt(args)?; 56 | let mut yes_no = false; 57 | let _ = terminal_input(&format!("{} (y/n)", prompt), |val| { 58 | yes_no = match val.as_str() { 59 | "y" => true, 60 | "n" => false, 61 | _ => return Err("invalid enter".to_string()), 62 | }; 63 | Ok(()) 64 | }); 65 | Ok((answer_id, json!({ "value": yes_no }))) 66 | } 67 | } 68 | 69 | #[async_trait::async_trait] 70 | impl DebotInterface for ConfirmInput { 71 | fn get_id(&self) -> String { 72 | ID.to_string() 73 | } 74 | 75 | fn get_abi(&self) -> Abi { 76 | Abi::Json(ABI.to_owned()) 77 | } 78 | 79 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 80 | match func { 81 | "get" => self.get(args), 82 | _ => Err(format!("function \"{}\" is not implemented", func)), 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/samples/Debot.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.35.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | 6 | abstract contract Debot { 7 | 8 | uint8 constant DEBOT_ABI = 1; 9 | 10 | uint8 m_options; 11 | optional(string) m_debotAbi; 12 | /// @notice Deprecated. For compatibility with old DEngine. 13 | optional(string) m_targetAbi; 14 | /// @notice Deprecated. For compatibility with old DEngine. 15 | optional(address) m_target; 16 | 17 | /* 18 | * Public debot interface 19 | */ 20 | 21 | /// @notice DeBot entry point. 22 | function start() public virtual; 23 | 24 | /// @notice Returns DeBot metadata. 25 | /// @return name String with name of debot, e.g. "DePool". 26 | /// @return version Semver version of debot, that will be converted to string like "x.y.z". 27 | /// @return publisher String with info about who has deployed debot to blokchain, e.g. "EverX". 28 | /// @return caption (10-20 ch.) String with short description, e.g. "Work with Smthg". 29 | /// @return author String with name of author of DeBot, e.g. "Ivan Ivanov". 30 | /// @return support Free TON address of author for questions and donations. 31 | /// @return hello String with first messsage with DeBot description. 32 | /// @return language (ISO-639) String with debot interface language, e.g. "en". 33 | /// @return dabi String with debot ABI. 34 | function getDebotInfo() public functionID(0xDEB) view virtual returns( 35 | string name, string version, string publisher, string caption, string author, 36 | address support, string hello, string language, string dabi, bytes icon); 37 | 38 | /// @notice Returns list of interfaces used by DeBot. 39 | function getRequiredInterfaces() public view virtual returns (uint256[] interfaces); 40 | 41 | /// @notice Returns DeBot ABI. 42 | /// Deprecated. 43 | function getDebotOptions() public view returns (uint8 options, string debotAbi, string targetAbi, address targetAddr) { 44 | debotAbi = m_debotAbi.hasValue() ? m_debotAbi.get() : ""; 45 | targetAbi = m_targetAbi.hasValue() ? m_targetAbi.get() : ""; 46 | targetAddr = m_target.hasValue() ? m_target.get() : address(0); 47 | options = m_options; 48 | } 49 | 50 | /// @notice Allow to set debot ABI. Do it before using debot. 51 | function setABI(string dabi) public { 52 | require(tvm.pubkey() == msg.pubkey(), 100); 53 | tvm.accept(); 54 | m_options |= DEBOT_ABI; 55 | m_debotAbi = dabi; 56 | } 57 | } -------------------------------------------------------------------------------- /tests/PipechainTest1.chain: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "debotAddress": "", 4 | "initMethod": "invokeTest", 5 | "initArgs": { 6 | "arg1": "1500000000", 7 | "arg2": "68656c6c6f20776f726c6421", 8 | "arg3": true, 9 | "arg4": 3, 10 | "arg5": "0:e859a5858fc99c8f6044aa179af68140c2fb9b07b3f52b70bef51e0c799fd2df", 11 | "arg6": "0x816747e3c1e0c3be11797a76ffd5f823a1c933586cac2f170bc1395f1f25e15b", 12 | "arg7": { 13 | "1": { 14 | "data": "10" 15 | }, 16 | "2": { 17 | "data": "2020" 18 | } 19 | } 20 | }, 21 | "abi": { 22 | "ABI version": 2, 23 | "header": [], 24 | "functions": [ 25 | { 26 | "name": "OnInvokeCompleted", 27 | "inputs": [ 28 | { 29 | "name": "status", 30 | "type": "uint8" 31 | }, 32 | { 33 | "components": [ 34 | { 35 | "name": "data", 36 | "type": "bytes" 37 | } 38 | ], 39 | "name": "ret1", 40 | "type": "map(uint32,tuple)" 41 | } 42 | ], 43 | "outputs": [] 44 | } 45 | ], 46 | "data": [], 47 | "events": [] 48 | }, 49 | "quiet": true, 50 | "chain": [ 51 | { 52 | "type": "Input", 53 | "interface": "a1d347099e29c1624c8890619daf207bde18e92df5220a54bcc6d858309ece84", 54 | "method": "get", 55 | "params": { 56 | "value": "1500000000" 57 | } 58 | }, 59 | { 60 | "type": "Input", 61 | "interface": "8796536366ee21852db56dccb60bc564598b618c865fc50c8b1ab740bba128e3", 62 | "method": "input", 63 | "params": { 64 | "value": "hello world!" 65 | } 66 | }, 67 | { 68 | "type": "Input", 69 | "interface": "16653eaf34c921467120f2685d425ff963db5cbb5aa676a62a2e33bfc3f6828a", 70 | "method": "get", 71 | "params": { 72 | "value": true 73 | } 74 | }, 75 | { 76 | "type": "Input", 77 | "interface": "ac1a4d3ecea232e49783df4a23a81823cdca3205dc58cd20c4db259c25605b48", 78 | "method": "select", 79 | "params": { 80 | "index": 3 81 | } 82 | }, 83 | { 84 | "type": "Input", 85 | "interface": "d7ed1bd8e6230871116f4522e58df0a93c5520c56f4ade23ef3d8919a984653b", 86 | "method": "get", 87 | "params": { 88 | "value": "0:e859a5858fc99c8f6044aa179af68140c2fb9b07b3f52b70bef51e0c799fd2df" 89 | } 90 | }, 91 | { 92 | "type": "Input", 93 | "interface": "a56115147709ed3437efb89460b94a120b7fe94379c795d1ebb0435a847ee580", 94 | "method": "getPublicKey", 95 | "params": { 96 | "value": "0x816747e3c1e0c3be11797a76ffd5f823a1c933586cac2f170bc1395f1f25e15b" 97 | } 98 | } 99 | ] 100 | } -------------------------------------------------------------------------------- /src/debot/interfaces/number_input.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{decode_answer_id, decode_int256, decode_prompt}; 2 | use crate::debot::term_browser::terminal_input; 3 | use ever_client::abi::Abi; 4 | use ever_client::debot::{DebotInterface, InterfaceResult}; 5 | use ever_client::encoding::decode_abi_bigint; 6 | use serde_json::{json, Value}; 7 | 8 | const ID: &str = "c5a9558b2664aed7dc3e6123436d544f13ffe69ab0e259412f48c6d1c8588401"; 9 | 10 | pub const ABI: &str = r#" 11 | { 12 | "ABI version": 2, 13 | "version": "2.2", 14 | "header": ["time"], 15 | "functions": [ 16 | { 17 | "name": "get", 18 | "id": "0x40f7a1ce", 19 | "inputs": [ 20 | {"name":"answerId","type":"uint32"}, 21 | {"name":"prompt","type":"string"}, 22 | {"name":"min","type":"int256"}, 23 | {"name":"max","type":"int256"} 24 | ], 25 | "outputs": [ 26 | {"name":"value","type":"int256"} 27 | ] 28 | }, 29 | { 30 | "name": "constructor", 31 | "id": "0x68b55f3f", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | ] 36 | } 37 | ], 38 | "data": [ 39 | ], 40 | "events": [ 41 | ], 42 | "fields": [ 43 | {"name":"_pubkey","type":"uint256"}, 44 | {"name":"_timestamp","type":"uint64"}, 45 | {"name":"_constructorFlag","type":"bool"} 46 | ] 47 | } 48 | "#; 49 | 50 | pub struct NumberInput {} 51 | 52 | impl NumberInput { 53 | pub fn new() -> Self { 54 | Self {} 55 | } 56 | fn get(&self, args: &Value) -> InterfaceResult { 57 | let answer_id = decode_answer_id(args)?; 58 | let prompt = decode_prompt(args)?; 59 | let min = decode_int256(args, "min")?; 60 | let max = decode_int256(args, "max")?; 61 | let prompt = format!("{}\n(>= {} and <= {})", prompt, min, max); 62 | let value = terminal_input(&prompt, |val| { 63 | let number = decode_abi_bigint(val.as_str()) 64 | .map_err(|e| format!("input is not a valid number: {}", e))?; 65 | if number < min || number > max { 66 | return Err("number is out of range".to_string()); 67 | } 68 | Ok(()) 69 | }); 70 | Ok((answer_id, json!({ "value": value }))) 71 | } 72 | } 73 | 74 | #[async_trait::async_trait] 75 | impl DebotInterface for NumberInput { 76 | fn get_id(&self) -> String { 77 | ID.to_string() 78 | } 79 | 80 | fn get_abi(&self) -> Abi { 81 | Abi::Json(ABI.to_owned()) 82 | } 83 | 84 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 85 | match func { 86 | "get" => self.get(args), 87 | _ => Err(format!("function \"{}\" is not implemented", func)), 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/debot/interfaces/address_input.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{decode_answer_id, decode_prompt}; 2 | use crate::config::Config; 3 | use crate::debot::term_browser::terminal_input; 4 | use crate::helpers::load_ton_address; 5 | use ever_client::abi::Abi; 6 | use ever_client::debot::{DebotInterface, InterfaceResult}; 7 | use serde_json::{json, Value}; 8 | 9 | const ID: &str = "d7ed1bd8e6230871116f4522e58df0a93c5520c56f4ade23ef3d8919a984653b"; 10 | 11 | pub const ABI: &str = r#" 12 | { 13 | "ABI version": 2, 14 | "version": "2.2", 15 | "header": ["time"], 16 | "functions": [ 17 | { 18 | "name": "get", 19 | "id": "0x210da005", 20 | "inputs": [ 21 | {"name":"answerId","type":"uint32"}, 22 | {"name":"prompt","type":"string"} 23 | ], 24 | "outputs": [ 25 | {"name":"value","type":"address"} 26 | ] 27 | }, 28 | { 29 | "name": "constructor", 30 | "id": "0x68b55f3f", 31 | "inputs": [ 32 | ], 33 | "outputs": [ 34 | ] 35 | } 36 | ], 37 | "data": [ 38 | ], 39 | "events": [ 40 | ], 41 | "fields": [ 42 | {"name":"_pubkey","type":"uint256"}, 43 | {"name":"_timestamp","type":"uint64"}, 44 | {"name":"_constructorFlag","type":"bool"} 45 | ] 46 | } 47 | "#; 48 | 49 | pub struct AddressInput { 50 | config: Config, 51 | } 52 | impl AddressInput { 53 | pub fn new(config: Config) -> Self { 54 | Self { config } 55 | } 56 | fn get(&self, args: &Value) -> InterfaceResult { 57 | let answer_id = decode_answer_id(args)?; 58 | let prompt = decode_prompt(args)?; 59 | let value = terminal_input(&prompt, |val| { 60 | let _ = load_ton_address(val, &self.config) 61 | .map_err(|e| format!("Invalid address: {}", e))?; 62 | Ok(()) 63 | }); 64 | Ok((answer_id, json!({ "value": value }))) 65 | } 66 | fn select(&self, args: &Value) -> InterfaceResult { 67 | let answer_id = decode_answer_id(args)?; 68 | let value = terminal_input("", |val| { 69 | let _ = load_ton_address(val, &self.config) 70 | .map_err(|e| format!("Invalid address: {}", e))?; 71 | Ok(()) 72 | }); 73 | Ok((answer_id, json!({ "value": value }))) 74 | } 75 | } 76 | 77 | #[async_trait::async_trait] 78 | impl DebotInterface for AddressInput { 79 | fn get_id(&self) -> String { 80 | ID.to_string() 81 | } 82 | 83 | fn get_abi(&self) -> Abi { 84 | Abi::Json(ABI.to_owned()) 85 | } 86 | 87 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 88 | match func { 89 | "get" => self.get(args), 90 | "select" => self.select(args), 91 | _ => Err(format!("function \"{}\" is not implemented", func)), 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/debot/interfaces/input_interface.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{decode_answer_id, decode_prompt, decode_string_arg}; 2 | use super::menu::{MenuItem, ID as MENU_ID}; 3 | use super::terminal::ID as TERMINAL_ID; 4 | use crate::debot::{ChainProcessor, ProcessorError}; 5 | use ever_client::abi::Abi; 6 | use ever_client::debot::{DebotInterface, InterfaceResult}; 7 | use serde_json::{json, Value}; 8 | use std::sync::Arc; 9 | use tokio::sync::RwLock; 10 | 11 | pub struct InputInterface { 12 | processor: Arc>, 13 | inner_interface: Arc, 14 | } 15 | 16 | impl InputInterface { 17 | pub fn new( 18 | inner_interface: Arc, 19 | processor: Arc>, 20 | ) -> Self { 21 | Self { 22 | inner_interface, 23 | processor, 24 | } 25 | } 26 | } 27 | 28 | #[async_trait::async_trait] 29 | impl DebotInterface for InputInterface { 30 | fn get_id(&self) -> String { 31 | self.inner_interface.get_id() 32 | } 33 | 34 | fn get_abi(&self) -> Abi { 35 | self.inner_interface.get_abi() 36 | } 37 | 38 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 39 | if self.get_id() == TERMINAL_ID && (func == "print" || func == "printf") { 40 | return self.inner_interface.call(func, args).await; 41 | } 42 | let result = self 43 | .processor 44 | .write() 45 | .await 46 | .next_input(&self.get_id(), func, args); 47 | match result { 48 | Err(ProcessorError::InterfaceCallNeeded) => { 49 | let res = self.inner_interface.call(func, args).await?; 50 | Ok(res) 51 | } 52 | Err(e) => return Err(format!("{:?}", e)), 53 | Ok(params) => { 54 | let prompt = decode_prompt(args); 55 | let title = decode_string_arg(args, "title"); 56 | let processor = self.processor.read().await; 57 | if let Ok(prompt) = prompt { 58 | processor.print(&prompt); 59 | } 60 | if let Ok(prompt) = title { 61 | processor.print(&prompt); 62 | } 63 | let params = params.unwrap_or(json!({})); 64 | if let Some(args) = params.as_object() { 65 | for arg in args { 66 | processor.print(&format!("{}", arg.1)); 67 | } 68 | } 69 | let answer_id = if self.get_id() == MENU_ID { 70 | let n = params["index"] 71 | .as_u64() 72 | .ok_or("invalid arguments for menu callback".to_string())?; 73 | let menu_items: Vec = 74 | serde_json::from_value(args["items"].clone()).map_err(|e| e.to_string())?; 75 | let menu = menu_items.get(n as usize); 76 | menu.ok_or("menu index is out of range".to_string())? 77 | .handler_id 78 | } else { 79 | decode_answer_id(args)? 80 | }; 81 | 82 | Ok((answer_id, params)) 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/samples/PipechainTest.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "setIcon", 7 | "inputs": [ 8 | {"name":"icon","type":"bytes"} 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "getDebotInfo", 15 | "id": "0xDEB", 16 | "inputs": [ 17 | ], 18 | "outputs": [ 19 | {"name":"name","type":"bytes"}, 20 | {"name":"version","type":"bytes"}, 21 | {"name":"publisher","type":"bytes"}, 22 | {"name":"caption","type":"bytes"}, 23 | {"name":"author","type":"bytes"}, 24 | {"name":"support","type":"address"}, 25 | {"name":"hello","type":"bytes"}, 26 | {"name":"language","type":"bytes"}, 27 | {"name":"dabi","type":"bytes"}, 28 | {"name":"icon","type":"bytes"} 29 | ] 30 | }, 31 | { 32 | "name": "getRequiredInterfaces", 33 | "inputs": [ 34 | ], 35 | "outputs": [ 36 | {"name":"interfaces","type":"uint256[]"} 37 | ] 38 | }, 39 | { 40 | "name": "start", 41 | "inputs": [ 42 | ], 43 | "outputs": [ 44 | ] 45 | }, 46 | { 47 | "name": "setSignBoxHandle", 48 | "inputs": [ 49 | {"name":"handle","type":"uint32"} 50 | ], 51 | "outputs": [ 52 | ] 53 | }, 54 | { 55 | "name": "onSuccess", 56 | "inputs": [ 57 | ], 58 | "outputs": [ 59 | ] 60 | }, 61 | { 62 | "name": "onError", 63 | "inputs": [ 64 | {"name":"sdkError","type":"uint32"}, 65 | {"name":"exitCode","type":"uint32"} 66 | ], 67 | "outputs": [ 68 | ] 69 | }, 70 | { 71 | "name": "setDataOnchain", 72 | "inputs": [ 73 | ], 74 | "outputs": [ 75 | ] 76 | }, 77 | { 78 | "name": "invokeTest", 79 | "inputs": [ 80 | {"name":"arg1","type":"uint64"}, 81 | {"name":"arg2","type":"bytes"}, 82 | {"name":"arg3","type":"bool"}, 83 | {"name":"arg4","type":"uint32"}, 84 | {"name":"arg5","type":"address"}, 85 | {"name":"arg6","type":"uint256"}, 86 | {"components":[{"name":"data","type":"bytes"}],"name":"arg7","type":"map(uint32,tuple)"} 87 | ], 88 | "outputs": [ 89 | ] 90 | }, 91 | { 92 | "name": "checkArg1", 93 | "inputs": [ 94 | {"name":"value","type":"uint128"} 95 | ], 96 | "outputs": [ 97 | ] 98 | }, 99 | { 100 | "name": "checkArg2", 101 | "inputs": [ 102 | {"name":"value","type":"bytes"} 103 | ], 104 | "outputs": [ 105 | ] 106 | }, 107 | { 108 | "name": "checkArg3", 109 | "inputs": [ 110 | {"name":"value","type":"bool"} 111 | ], 112 | "outputs": [ 113 | ] 114 | }, 115 | { 116 | "name": "checkArg4", 117 | "inputs": [ 118 | {"name":"index","type":"uint32"} 119 | ], 120 | "outputs": [ 121 | ] 122 | }, 123 | { 124 | "name": "checkArg5", 125 | "inputs": [ 126 | {"name":"value","type":"address"} 127 | ], 128 | "outputs": [ 129 | ] 130 | }, 131 | { 132 | "name": "checkArg6", 133 | "inputs": [ 134 | {"name":"value","type":"uint256"} 135 | ], 136 | "outputs": [ 137 | ] 138 | }, 139 | { 140 | "name": "getDebotOptions", 141 | "inputs": [ 142 | ], 143 | "outputs": [ 144 | {"name":"options","type":"uint8"}, 145 | {"name":"debotAbi","type":"bytes"}, 146 | {"name":"targetAbi","type":"bytes"}, 147 | {"name":"targetAddr","type":"address"} 148 | ] 149 | }, 150 | { 151 | "name": "setABI", 152 | "inputs": [ 153 | {"name":"dabi","type":"bytes"} 154 | ], 155 | "outputs": [ 156 | ] 157 | }, 158 | { 159 | "name": "constructor", 160 | "inputs": [ 161 | ], 162 | "outputs": [ 163 | ] 164 | } 165 | ], 166 | "data": [ 167 | ], 168 | "events": [ 169 | ] 170 | } 171 | -------------------------------------------------------------------------------- /tests/samples/PipechainTest_2.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "setIcon", 7 | "inputs": [ 8 | {"name":"icon","type":"bytes"} 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "getDebotInfo", 15 | "id": "0xDEB", 16 | "inputs": [ 17 | ], 18 | "outputs": [ 19 | {"name":"name","type":"bytes"}, 20 | {"name":"version","type":"bytes"}, 21 | {"name":"publisher","type":"bytes"}, 22 | {"name":"caption","type":"bytes"}, 23 | {"name":"author","type":"bytes"}, 24 | {"name":"support","type":"address"}, 25 | {"name":"hello","type":"bytes"}, 26 | {"name":"language","type":"bytes"}, 27 | {"name":"dabi","type":"bytes"}, 28 | {"name":"icon","type":"bytes"} 29 | ] 30 | }, 31 | { 32 | "name": "getRequiredInterfaces", 33 | "inputs": [ 34 | ], 35 | "outputs": [ 36 | {"name":"interfaces","type":"uint256[]"} 37 | ] 38 | }, 39 | { 40 | "name": "start", 41 | "inputs": [ 42 | ], 43 | "outputs": [ 44 | ] 45 | }, 46 | { 47 | "name": "setSignBoxHandle", 48 | "inputs": [ 49 | {"name":"handle","type":"uint32"} 50 | ], 51 | "outputs": [ 52 | ] 53 | }, 54 | { 55 | "name": "onSuccess", 56 | "inputs": [ 57 | ], 58 | "outputs": [ 59 | ] 60 | }, 61 | { 62 | "name": "onError", 63 | "inputs": [ 64 | {"name":"sdkError","type":"uint32"}, 65 | {"name":"exitCode","type":"uint32"} 66 | ], 67 | "outputs": [ 68 | ] 69 | }, 70 | { 71 | "name": "setDataOnchain", 72 | "inputs": [ 73 | ], 74 | "outputs": [ 75 | ] 76 | }, 77 | { 78 | "name": "invokeTest", 79 | "inputs": [ 80 | {"name":"arg1","type":"uint64"}, 81 | {"name":"arg2","type":"bytes"}, 82 | {"name":"arg3","type":"bool"}, 83 | {"name":"arg4","type":"uint32"}, 84 | {"name":"arg5","type":"address"}, 85 | {"name":"arg6","type":"uint256"}, 86 | {"components":[{"name":"data","type":"bytes"}],"name":"arg7","type":"map(uint32,tuple)"} 87 | ], 88 | "outputs": [ 89 | ] 90 | }, 91 | { 92 | "name": "checkArg1", 93 | "inputs": [ 94 | {"name":"value","type":"uint128"} 95 | ], 96 | "outputs": [ 97 | ] 98 | }, 99 | { 100 | "name": "checkArg2", 101 | "inputs": [ 102 | {"name":"value","type":"bytes"} 103 | ], 104 | "outputs": [ 105 | ] 106 | }, 107 | { 108 | "name": "checkArg3", 109 | "inputs": [ 110 | {"name":"value","type":"bool"} 111 | ], 112 | "outputs": [ 113 | ] 114 | }, 115 | { 116 | "name": "checkArg4", 117 | "inputs": [ 118 | {"name":"index","type":"uint32"} 119 | ], 120 | "outputs": [ 121 | ] 122 | }, 123 | { 124 | "name": "checkArg5", 125 | "inputs": [ 126 | {"name":"value","type":"address"} 127 | ], 128 | "outputs": [ 129 | ] 130 | }, 131 | { 132 | "name": "checkArg6", 133 | "inputs": [ 134 | {"name":"value","type":"uint256"} 135 | ], 136 | "outputs": [ 137 | ] 138 | }, 139 | { 140 | "name": "getDebotOptions", 141 | "inputs": [ 142 | ], 143 | "outputs": [ 144 | {"name":"options","type":"uint8"}, 145 | {"name":"debotAbi","type":"bytes"}, 146 | {"name":"targetAbi","type":"bytes"}, 147 | {"name":"targetAddr","type":"address"} 148 | ] 149 | }, 150 | { 151 | "name": "setABI", 152 | "inputs": [ 153 | {"name":"dabi","type":"bytes"} 154 | ], 155 | "outputs": [ 156 | ] 157 | }, 158 | { 159 | "name": "constructor", 160 | "inputs": [ 161 | ], 162 | "outputs": [ 163 | ] 164 | } 165 | ], 166 | "data": [ 167 | ], 168 | "events": [ 169 | ] 170 | } 171 | -------------------------------------------------------------------------------- /src/debot/interfaces/signing_box_input.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{decode_answer_id, decode_array, decode_prompt}; 2 | 3 | use crate::debot::term_signing_box::TerminalSigningBox; 4 | use crate::debot::{ChainProcessor, ProcessorError}; 5 | use crate::helpers::TonClient; 6 | use ever_client::abi::Abi; 7 | use ever_client::debot::{DebotInterface, InterfaceResult}; 8 | use ever_client::encoding::decode_abi_bigint; 9 | use serde_json::json; 10 | use serde_json::Value; 11 | use std::sync::Arc; 12 | use tokio::sync::RwLock; 13 | 14 | pub const ID: &str = "c13024e101c95e71afb1f5fa6d72f633d51e721de0320d73dfd6121a54e4d40a"; 15 | 16 | const ABI: &str = r#" 17 | { 18 | "ABI version": 2, 19 | "version": "2.2", 20 | "header": ["time"], 21 | "functions": [ 22 | { 23 | "name": "get", 24 | "id": "0x04895be9", 25 | "inputs": [ 26 | {"name":"answerId","type":"uint32"}, 27 | {"name":"prompt","type":"string"}, 28 | {"name":"possiblePublicKeys","type":"uint256[]"} 29 | ], 30 | "outputs": [ 31 | {"name":"handle","type":"uint32"} 32 | ] 33 | }, 34 | { 35 | "name": "constructor", 36 | "id": "0x68b55f3f", 37 | "inputs": [ 38 | ], 39 | "outputs": [ 40 | ] 41 | } 42 | ], 43 | "data": [ 44 | ], 45 | "events": [ 46 | ], 47 | "fields": [ 48 | {"name":"_pubkey","type":"uint256"}, 49 | {"name":"_timestamp","type":"uint64"}, 50 | {"name":"_constructorFlag","type":"bool"} 51 | ] 52 | } 53 | "#; 54 | 55 | pub struct SigningBoxInput { 56 | handles: RwLock>, 57 | client: TonClient, 58 | processor: Arc>, 59 | } 60 | impl SigningBoxInput { 61 | pub fn new(client: TonClient, processor: Arc>) -> Self { 62 | Self { 63 | handles: RwLock::new(vec![]), 64 | client, 65 | processor, 66 | } 67 | } 68 | 69 | async fn get(&self, args: &Value) -> InterfaceResult { 70 | let answer_id = decode_answer_id(args)?; 71 | let prompt = decode_prompt(args)?; 72 | let possible_keys = decode_array(args, "possiblePublicKeys", |elem| { 73 | decode_abi_bigint(elem.as_str()?).ok()?; 74 | Some(elem.as_str().unwrap().to_string()) 75 | })?; 76 | println!("{}", prompt); 77 | let result = self.processor.write().await.next_signing_box(); 78 | match result { 79 | Err(ProcessorError::InterfaceCallNeeded) => { 80 | let signing_box = 81 | TerminalSigningBox::new::<&[u8]>(self.client.clone(), possible_keys, None) 82 | .await?; 83 | let handle = signing_box.handle(); 84 | self.handles.write().await.push(signing_box); 85 | Ok((answer_id, json!({ "handle": handle.0}))) 86 | } 87 | Err(e) => Err(format!("{:?}", e)), 88 | Ok(handle) => Ok((answer_id, json!({ "handle": handle}))), 89 | } 90 | } 91 | } 92 | 93 | #[async_trait::async_trait] 94 | impl DebotInterface for SigningBoxInput { 95 | fn get_id(&self) -> String { 96 | ID.to_string() 97 | } 98 | 99 | fn get_abi(&self) -> Abi { 100 | Abi::Json(ABI.to_owned()) 101 | } 102 | 103 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 104 | match func { 105 | "get" => self.get(args).await, 106 | _ => Err(format!("function \"{}\" is not implemented", func)), 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/samples/SafeMultisigWallet.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "constructor", 7 | "inputs": [ 8 | {"name":"owners","type":"uint256[]"}, 9 | {"name":"reqConfirms","type":"uint8"} 10 | ], 11 | "outputs": [ 12 | ] 13 | }, 14 | { 15 | "name": "acceptTransfer", 16 | "inputs": [ 17 | {"name":"payload","type":"bytes"} 18 | ], 19 | "outputs": [ 20 | ] 21 | }, 22 | { 23 | "name": "sendTransaction", 24 | "inputs": [ 25 | {"name":"dest","type":"address"}, 26 | {"name":"value","type":"uint128"}, 27 | {"name":"bounce","type":"bool"}, 28 | {"name":"flags","type":"uint8"}, 29 | {"name":"payload","type":"cell"} 30 | ], 31 | "outputs": [ 32 | ] 33 | }, 34 | { 35 | "name": "submitTransaction", 36 | "inputs": [ 37 | {"name":"dest","type":"address"}, 38 | {"name":"value","type":"uint128"}, 39 | {"name":"bounce","type":"bool"}, 40 | {"name":"allBalance","type":"bool"}, 41 | {"name":"payload","type":"cell"} 42 | ], 43 | "outputs": [ 44 | {"name":"transId","type":"uint64"} 45 | ] 46 | }, 47 | { 48 | "name": "confirmTransaction", 49 | "inputs": [ 50 | {"name":"transactionId","type":"uint64"} 51 | ], 52 | "outputs": [ 53 | ] 54 | }, 55 | { 56 | "name": "isConfirmed", 57 | "inputs": [ 58 | {"name":"mask","type":"uint32"}, 59 | {"name":"index","type":"uint8"} 60 | ], 61 | "outputs": [ 62 | {"name":"confirmed","type":"bool"} 63 | ] 64 | }, 65 | { 66 | "name": "getParameters", 67 | "inputs": [ 68 | ], 69 | "outputs": [ 70 | {"name":"maxQueuedTransactions","type":"uint8"}, 71 | {"name":"maxCustodianCount","type":"uint8"}, 72 | {"name":"expirationTime","type":"uint64"}, 73 | {"name":"minValue","type":"uint128"}, 74 | {"name":"requiredTxnConfirms","type":"uint8"} 75 | ] 76 | }, 77 | { 78 | "name": "getTransaction", 79 | "inputs": [ 80 | {"name":"transactionId","type":"uint64"} 81 | ], 82 | "outputs": [ 83 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"}],"name":"trans","type":"tuple"} 84 | ] 85 | }, 86 | { 87 | "name": "getTransactions", 88 | "inputs": [ 89 | ], 90 | "outputs": [ 91 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"}],"name":"transactions","type":"tuple[]"} 92 | ] 93 | }, 94 | { 95 | "name": "getTransactionIds", 96 | "inputs": [ 97 | ], 98 | "outputs": [ 99 | {"name":"ids","type":"uint64[]"} 100 | ] 101 | }, 102 | { 103 | "name": "getCustodians", 104 | "inputs": [ 105 | ], 106 | "outputs": [ 107 | {"components":[{"name":"index","type":"uint8"},{"name":"pubkey","type":"uint256"}],"name":"custodians","type":"tuple[]"} 108 | ] 109 | } 110 | ], 111 | "data": [ 112 | ], 113 | "events": [ 114 | { 115 | "name": "TransferAccepted", 116 | "inputs": [ 117 | {"name":"payload","type":"bytes"} 118 | ], 119 | "outputs": [ 120 | ] 121 | } 122 | ] 123 | } 124 | -------------------------------------------------------------------------------- /src/debot/interfaces/amount_input.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{decode_answer_id, decode_num_arg, decode_prompt}; 2 | use crate::convert; 3 | use crate::debot::term_browser::terminal_input; 4 | use ever_client::abi::Abi; 5 | use ever_client::debot::{DebotInterface, InterfaceResult}; 6 | use ever_client::encoding::decode_abi_number; 7 | use serde_json::{json, Value}; 8 | 9 | const ID: &str = "a1d347099e29c1624c8890619daf207bde18e92df5220a54bcc6d858309ece84"; 10 | 11 | pub const ABI: &str = r#" 12 | { 13 | "ABI version": 2, 14 | "version": "2.2", 15 | "header": ["time"], 16 | "functions": [ 17 | { 18 | "name": "get", 19 | "id": "0x16740bd3", 20 | "inputs": [ 21 | {"name":"answerId","type":"uint32"}, 22 | {"name":"prompt","type":"string"}, 23 | {"name":"decimals","type":"uint8"}, 24 | {"name":"min","type":"uint128"}, 25 | {"name":"max","type":"uint128"} 26 | ], 27 | "outputs": [ 28 | {"name":"value","type":"uint128"} 29 | ] 30 | }, 31 | { 32 | "name": "constructor", 33 | "id": "0x68b55f3f", 34 | "inputs": [ 35 | ], 36 | "outputs": [ 37 | ] 38 | } 39 | ], 40 | "data": [ 41 | ], 42 | "events": [ 43 | ], 44 | "fields": [ 45 | {"name":"_pubkey","type":"uint256"}, 46 | {"name":"_timestamp","type":"uint64"}, 47 | {"name":"_constructorFlag","type":"bool"} 48 | ] 49 | } 50 | "#; 51 | 52 | pub struct AmountInput {} 53 | 54 | impl AmountInput { 55 | pub fn new() -> Self { 56 | Self {} 57 | } 58 | fn get(&self, args: &Value) -> InterfaceResult { 59 | let answer_id = decode_answer_id(args)?; 60 | let prompt = decode_prompt(args)?; 61 | let decimals = decode_num_arg::(args, "decimals")?; 62 | if decimals > 255 { 63 | return Err(format!("too many decimals ({})", decimals)); 64 | } 65 | let min = decode_num_arg::(args, "min")?; 66 | let max = decode_num_arg::(args, "max")?; 67 | let mut value = String::new(); 68 | 69 | let prompt = format!( 70 | "{}\n(>= {} and <= {})", 71 | prompt, 72 | format_amount(min, decimals), 73 | format_amount(max, decimals) 74 | ); 75 | let _ = terminal_input(&prompt, |val| { 76 | value = convert::convert_amount(val.as_str(), decimals)?; 77 | let number = decode_abi_number::(&value) 78 | .map_err(|e| format!("input is not a valid amount: {}", e))?; 79 | if number < min || number > max { 80 | return Err("amount is out of range".to_string()); 81 | } 82 | Ok(()) 83 | }); 84 | Ok((answer_id, json!({ "value": value }))) 85 | } 86 | } 87 | 88 | #[async_trait::async_trait] 89 | impl DebotInterface for AmountInput { 90 | fn get_id(&self) -> String { 91 | ID.to_string() 92 | } 93 | 94 | fn get_abi(&self) -> Abi { 95 | Abi::Json(ABI.to_owned()) 96 | } 97 | 98 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 99 | match func { 100 | "get" => self.get(args), 101 | _ => Err(format!("function \"{}\" is not implemented", func)), 102 | } 103 | } 104 | } 105 | 106 | fn format_amount(amount: u128, decimals: usize) -> String { 107 | if decimals == 0 { 108 | format!("{}", amount) 109 | } else { 110 | let integer = amount / 10u128.pow(decimals as u32); 111 | let float = amount - integer * 10u128.pow(decimals as u32); 112 | format!("{}.{:0>width$}", integer, float, width = decimals) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/samples/sample3.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "start", 7 | "inputs": [ 8 | ], 9 | "outputs": [ 10 | ] 11 | }, 12 | { 13 | "name": "getRandom", 14 | "inputs": [ 15 | {"name":"buffer","type":"bytes"} 16 | ], 17 | "outputs": [ 18 | ] 19 | }, 20 | { 21 | "name": "runEncryptionBoxInput", 22 | "inputs": [ 23 | ], 24 | "outputs": [ 25 | ] 26 | }, 27 | { 28 | "name": "chachaBoxHandle", 29 | "inputs": [ 30 | {"name":"handle","type":"uint32"} 31 | ], 32 | "outputs": [ 33 | ] 34 | }, 35 | { 36 | "name": "decrypt", 37 | "inputs": [ 38 | {"name":"result","type":"uint32"}, 39 | {"name":"encrypted","type":"bytes"} 40 | ], 41 | "outputs": [ 42 | ] 43 | }, 44 | { 45 | "name": "checkResult", 46 | "inputs": [ 47 | {"name":"result","type":"uint32"}, 48 | {"name":"decrypted","type":"bytes"} 49 | ], 50 | "outputs": [ 51 | ] 52 | }, 53 | { 54 | "name": "regenerate", 55 | "inputs": [ 56 | ], 57 | "outputs": [ 58 | ] 59 | }, 60 | { 61 | "name": "getSecretNaClBox", 62 | "inputs": [ 63 | {"name":"buffer","type":"bytes"} 64 | ], 65 | "outputs": [ 66 | ] 67 | }, 68 | { 69 | "name": "naclSecretBoxHandle", 70 | "inputs": [ 71 | {"name":"handle","type":"uint32"} 72 | ], 73 | "outputs": [ 74 | ] 75 | }, 76 | { 77 | "name": "decryptSecretNaCl", 78 | "inputs": [ 79 | {"name":"result","type":"uint32"}, 80 | {"name":"encrypted","type":"bytes"} 81 | ], 82 | "outputs": [ 83 | ] 84 | }, 85 | { 86 | "name": "checkSecretNaClResult", 87 | "inputs": [ 88 | {"name":"result","type":"uint32"}, 89 | {"name":"decrypted","type":"bytes"} 90 | ], 91 | "outputs": [ 92 | ] 93 | }, 94 | { 95 | "name": "getNaClBox", 96 | "inputs": [ 97 | ], 98 | "outputs": [ 99 | ] 100 | }, 101 | { 102 | "name": "naclBoxHandle", 103 | "inputs": [ 104 | {"name":"handle","type":"uint32"} 105 | ], 106 | "outputs": [ 107 | ] 108 | }, 109 | { 110 | "name": "decryptNaCl", 111 | "inputs": [ 112 | {"name":"result","type":"uint32"}, 113 | {"name":"encrypted","type":"bytes"} 114 | ], 115 | "outputs": [ 116 | ] 117 | }, 118 | { 119 | "name": "checkNaClResult", 120 | "inputs": [ 121 | {"name":"result","type":"uint32"}, 122 | {"name":"decrypted","type":"bytes"} 123 | ], 124 | "outputs": [ 125 | ] 126 | }, 127 | { 128 | "name": "getDebotInfo", 129 | "id": "0xDEB", 130 | "inputs": [ 131 | ], 132 | "outputs": [ 133 | {"name":"name","type":"bytes"}, 134 | {"name":"version","type":"bytes"}, 135 | {"name":"publisher","type":"bytes"}, 136 | {"name":"caption","type":"bytes"}, 137 | {"name":"author","type":"bytes"}, 138 | {"name":"support","type":"address"}, 139 | {"name":"hello","type":"bytes"}, 140 | {"name":"language","type":"bytes"}, 141 | {"name":"dabi","type":"bytes"}, 142 | {"name":"icon","type":"bytes"} 143 | ] 144 | }, 145 | { 146 | "name": "getRequiredInterfaces", 147 | "inputs": [ 148 | ], 149 | "outputs": [ 150 | {"name":"interfaces","type":"uint256[]"} 151 | ] 152 | }, 153 | { 154 | "name": "getDebotOptions", 155 | "inputs": [ 156 | ], 157 | "outputs": [ 158 | {"name":"options","type":"uint8"}, 159 | {"name":"debotAbi","type":"bytes"}, 160 | {"name":"targetAbi","type":"bytes"}, 161 | {"name":"targetAddr","type":"address"} 162 | ] 163 | }, 164 | { 165 | "name": "setABI", 166 | "inputs": [ 167 | {"name":"dabi","type":"bytes"} 168 | ], 169 | "outputs": [ 170 | ] 171 | }, 172 | { 173 | "name": "constructor", 174 | "inputs": [ 175 | ], 176 | "outputs": [ 177 | ] 178 | } 179 | ], 180 | "data": [ 181 | ], 182 | "events": [ 183 | ] 184 | } 185 | -------------------------------------------------------------------------------- /tests/depool.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.1", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "sendAnswer", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "addOrdinaryStake", 15 | "inputs": [ 16 | {"name":"stake","type":"uint64"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "addVestingStake", 23 | "inputs": [ 24 | {"name":"stake","type":"uint64"}, 25 | {"name":"beneficiary","type":"address"}, 26 | {"name":"withdrawalPeriod","type":"uint32"}, 27 | {"name":"totalPeriod","type":"uint32"} 28 | ], 29 | "outputs": [ 30 | ] 31 | }, 32 | { 33 | "name": "addLockStake", 34 | "inputs": [ 35 | {"name":"stake","type":"uint64"}, 36 | {"name":"beneficiary","type":"address"}, 37 | {"name":"withdrawalPeriod","type":"uint32"}, 38 | {"name":"totalPeriod","type":"uint32"} 39 | ], 40 | "outputs": [ 41 | ] 42 | }, 43 | { 44 | "name": "withdrawFromPoolingRound", 45 | "inputs": [ 46 | {"name":"withdrawValue","type":"uint64"} 47 | ], 48 | "outputs": [ 49 | ] 50 | }, 51 | { 52 | "name": "withdrawPart", 53 | "inputs": [ 54 | {"name":"withdrawValue","type":"uint64"} 55 | ], 56 | "outputs": [ 57 | ] 58 | }, 59 | { 60 | "name": "withdrawAll", 61 | "inputs": [ 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "cancelWithdrawal", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | }, 73 | { 74 | "name": "transferStake", 75 | "inputs": [ 76 | {"name":"dest","type":"address"}, 77 | {"name":"amount","type":"uint64"} 78 | ], 79 | "outputs": [ 80 | ] 81 | }, 82 | { 83 | "name": "ticktock", 84 | "inputs": [ 85 | ], 86 | "outputs": [ 87 | ] 88 | }, 89 | { 90 | "name": "receiveFunds", 91 | "inputs": [ 92 | ], 93 | "outputs": [ 94 | ] 95 | }, 96 | { 97 | "name": "getData", 98 | "inputs": [ 99 | ], 100 | "outputs": [ 101 | {"name":"stake","type":"uint64"}, 102 | {"name":"sender","type":"address"}, 103 | {"name":"receiver","type":"address"}, 104 | {"name":"withdrawal","type":"uint32"}, 105 | {"name":"total","type":"uint32"}, 106 | {"name":"reinvest","type":"bool"}, 107 | {"name":"value","type":"uint128"} 108 | ] 109 | }, 110 | { 111 | "name": "setVestingDonor", 112 | "inputs": [ 113 | {"name":"donor","type":"address"} 114 | ], 115 | "outputs": [ 116 | ] 117 | }, 118 | { 119 | "name": "setLockDonor", 120 | "inputs": [ 121 | {"name":"donor","type":"address"} 122 | ], 123 | "outputs": [ 124 | ] 125 | }, 126 | { 127 | "name": "error", 128 | "inputs": [ 129 | {"name":"code","type":"uint256"} 130 | ], 131 | "outputs": [ 132 | ] 133 | }, 134 | { 135 | "name": "outOfGas", 136 | "inputs": [ 137 | ], 138 | "outputs": [ 139 | ] 140 | }, 141 | { 142 | "name": "constructor", 143 | "inputs": [ 144 | ], 145 | "outputs": [ 146 | ] 147 | } 148 | ], 149 | "data": [ 150 | {"key":1,"name":"m_seed","type":"uint256"} 151 | ], 152 | "events": [ 153 | { 154 | "name": "StakeSigningRequested", 155 | "inputs": [ 156 | {"name":"electionId","type":"uint32"}, 157 | {"name":"proxy","type":"address"} 158 | ], 159 | "outputs": [ 160 | ] 161 | } 162 | ], 163 | "fields": [ 164 | {"name":"_pubkey","type":"uint256"}, 165 | {"name":"_timestamp","type":"uint64"}, 166 | {"name":"_constructorFlag","type":"bool"}, 167 | {"name":"m_seed","type":"uint256"}, 168 | {"name":"m_stake","type":"uint64"}, 169 | {"name":"m_sender","type":"address"}, 170 | {"name":"m_receiver","type":"address"}, 171 | {"name":"m_withdrawal","type":"uint32"}, 172 | {"name":"m_total","type":"uint32"}, 173 | {"name":"m_reinvest","type":"bool"}, 174 | {"name":"m_value","type":"uint128"} 175 | ] 176 | } 177 | -------------------------------------------------------------------------------- /tests/samples/fakeDepool.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "sendAnswer", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "addOrdinaryStake", 15 | "inputs": [ 16 | {"name":"stake","type":"uint64"} 17 | ], 18 | "outputs": [ 19 | ] 20 | }, 21 | { 22 | "name": "addVestingStake", 23 | "inputs": [ 24 | {"name":"stake","type":"uint64"}, 25 | {"name":"beneficiary","type":"address"}, 26 | {"name":"withdrawalPeriod","type":"uint32"}, 27 | {"name":"totalPeriod","type":"uint32"} 28 | ], 29 | "outputs": [ 30 | ] 31 | }, 32 | { 33 | "name": "addLockStake", 34 | "inputs": [ 35 | {"name":"stake","type":"uint64"}, 36 | {"name":"beneficiary","type":"address"}, 37 | {"name":"withdrawalPeriod","type":"uint32"}, 38 | {"name":"totalPeriod","type":"uint32"} 39 | ], 40 | "outputs": [ 41 | ] 42 | }, 43 | { 44 | "name": "withdrawFromPoolingRound", 45 | "inputs": [ 46 | {"name":"withdrawValue","type":"uint64"} 47 | ], 48 | "outputs": [ 49 | ] 50 | }, 51 | { 52 | "name": "withdrawPart", 53 | "inputs": [ 54 | {"name":"withdrawValue","type":"uint64"} 55 | ], 56 | "outputs": [ 57 | ] 58 | }, 59 | { 60 | "name": "withdrawAll", 61 | "inputs": [ 62 | ], 63 | "outputs": [ 64 | ] 65 | }, 66 | { 67 | "name": "cancelWithdrawal", 68 | "inputs": [ 69 | ], 70 | "outputs": [ 71 | ] 72 | }, 73 | { 74 | "name": "transferStake", 75 | "inputs": [ 76 | {"name":"dest","type":"address"}, 77 | {"name":"amount","type":"uint64"} 78 | ], 79 | "outputs": [ 80 | ] 81 | }, 82 | { 83 | "name": "ticktock", 84 | "inputs": [ 85 | ], 86 | "outputs": [ 87 | ] 88 | }, 89 | { 90 | "name": "receiveFunds", 91 | "inputs": [ 92 | ], 93 | "outputs": [ 94 | ] 95 | }, 96 | { 97 | "name": "getData", 98 | "inputs": [ 99 | ], 100 | "outputs": [ 101 | {"name":"stake","type":"uint64"}, 102 | {"name":"sender","type":"address"}, 103 | {"name":"receiver","type":"address"}, 104 | {"name":"withdrawal","type":"uint32"}, 105 | {"name":"total","type":"uint32"}, 106 | {"name":"reinvest","type":"bool"}, 107 | {"name":"value","type":"uint128"} 108 | ] 109 | }, 110 | { 111 | "name": "setVestingDonor", 112 | "inputs": [ 113 | {"name":"donor","type":"address"} 114 | ], 115 | "outputs": [ 116 | ] 117 | }, 118 | { 119 | "name": "setLockDonor", 120 | "inputs": [ 121 | {"name":"donor","type":"address"} 122 | ], 123 | "outputs": [ 124 | ] 125 | }, 126 | { 127 | "name": "error", 128 | "inputs": [ 129 | {"name":"code","type":"uint256"} 130 | ], 131 | "outputs": [ 132 | ] 133 | }, 134 | { 135 | "name": "outOfGas", 136 | "inputs": [ 137 | ], 138 | "outputs": [ 139 | ] 140 | }, 141 | { 142 | "name": "constructor", 143 | "inputs": [ 144 | ], 145 | "outputs": [ 146 | ] 147 | } 148 | ], 149 | "data": [ 150 | {"key":1,"name":"m_seed","type":"uint256"} 151 | ], 152 | "events": [ 153 | { 154 | "name": "StakeSigningRequested", 155 | "inputs": [ 156 | {"name":"electionId","type":"uint32"}, 157 | {"name":"proxy","type":"address"} 158 | ], 159 | "outputs": [ 160 | ] 161 | } 162 | ], 163 | "fields": [ 164 | {"name":"_pubkey","type":"uint256"}, 165 | {"name":"_timestamp","type":"uint64"}, 166 | {"name":"_constructorFlag","type":"bool"}, 167 | {"name":"m_seed","type":"uint256"}, 168 | {"name":"m_stake","type":"uint64"}, 169 | {"name":"m_sender","type":"address"}, 170 | {"name":"m_receiver","type":"address"}, 171 | {"name":"m_withdrawal","type":"uint32"}, 172 | {"name":"m_total","type":"uint32"}, 173 | {"name":"m_reinvest","type":"bool"}, 174 | {"name":"m_value","type":"uint128"} 175 | ] 176 | } 177 | -------------------------------------------------------------------------------- /Debug.md: -------------------------------------------------------------------------------- 1 | # How to debug contracts with ever-cli 2 | 3 | ever-cli can help user investigate at what moment of contract execution has error happened. 4 | 5 | # 1. Preliminary actions 6 | 7 | User should generate debug info file to bind contract and source files on the stage of contract compilation with 8 | [tvm-linker](https://github.com/everx-labs/TVM-linker#1-generating-a-ready-to-deploy-contract): 9 | 10 | ```bash 11 | $ tvm_linker compile .code --abi-json .abi.json --debug-map .dbg.json -o .tvc --lib stdlib_sol.tvm 12 | ``` 13 | 14 | Besides **.tvc** file pay attention to the **.dbg.json** file, it contains debug information. To generate it, option 15 | `--debug-map` must be specified. 16 | 17 | # 2. Deploy your contract 18 | 19 | ```bash 20 | $ ever-cli deploy --wc --abi .abi.json --sign .tvc 21 | ``` 22 | 23 | Here user obtains contract address, which will be denoted as `
` in further instruction. 24 | Before actual deploy or if deploy failed user can debug deploy with ever-cli command which has almost the same options: 25 | 26 | ```bash 27 | $ ever-cli debug deploy --wc --abi .abi.json --sign -d .dbg.json .tvc [--init_balance] [-o ] 28 | ``` 29 | 30 | `--init_balance` option allows to debug deploy without preliminary initiating balance of the network address. 31 | After successful execution a log file will be created (path to this file can be set with `-o ` option, 32 | default path is `./trace.log`) 33 | 34 | # 3. Make a local test call or run of contract function 35 | 36 | Before calling contract function onchain, user can run function call locally with trace. 37 | 38 | ```bash 39 | ever-cli debug call --abi .abi.json --sign -d .dbg.json [-o ]
40 | ``` 41 | 42 | The same as call, run command can also be debugged: 43 | 44 | ```bash 45 | ever-cli debug run --abi .abi.json -d .dbg.json [-o ]
46 | ``` 47 | 48 | As in previous case log will be saved to a specified path. 49 | 50 | # 4. Debug a real contract error 51 | 52 | 1) Contract call fails due to unknown error. 53 | 2) Explore the error message, look for this string: 54 | 55 | ``` 56 | Error: Failed: { 57 | ... 58 | "transaction_id": "69a8250000571041c011ef717228f6637b836248f8af46755c33bc9bcf0e9b88" 59 | ``` 60 | 61 | Or get transaction ID from [Live](https://ever.live/landing). 62 | 63 | 3) Run the ever-cli debug transaction command with the obtained value to get TVM trace: 64 | 65 | ``` 66 | ever-cli debug transaction 69a8250000571041c011ef717228f6637b836248f8af46755c33bc9bcf0e9b88 \ 67 | --dump_contract -e --min_trace -d .dbg.json -o trace_old_code.log 68 | ``` 69 | 70 | 4) Explore the output for contract dump: 71 | 72 | ``` 73 | ... 74 | Contract account was dumped to 0:8be07ec3f8f25ebb35ce1a29d48b0cbbf1d41aa00249f34e89f136c561cae3fa-69a8250000571041c011ef717228f6637b836248f8af46755c33bc9bcf0e9b88.boc 75 | ... 76 | ``` 77 | 78 | 5) Rewrite your contract to fix the error and compile a new version of the contract. 79 | 6) Replace code in the account dump using tvm_linker: 80 | 81 | ``` 82 | tvm_linker replace_code -a .abi.json --debug-map .dbg.json -o contract.boc .code "0:8be07ec3f8f25ebb35ce1a29d48b0cbbf1d41aa00249f34e89f136c561cae3fa-69a8250000571041c011ef717228f6637b836248f8af46755c33bc9bcf0e9b88.boc" 83 | ``` 84 | 85 | 7.1) Run debug replay to replay the transaction on the modified account state: 86 | 87 | ``` 88 | ever-cli debug replay --update_state -d .dbg.json -o new_trace.log 69a8250000571041c011ef717228f6637b836248f8af46755c33bc9bcf0e9b88 contract.boc" 89 | ``` 90 | 91 | 7.2) Run debug call locally on the new account to test new version of the contract on a new generated call message: 92 | 93 | ``` 94 | ever-cli debug call --boc --abi .abi.json -d .dbg.json -o new_trace.log --sign contract.boc 95 | ``` 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/debot/interfaces/menu.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::decode_string_arg; 2 | use crate::debot::term_browser::action_input; 3 | use ever_client::abi::Abi; 4 | use ever_client::debot::{DebotInterface, InterfaceResult}; 5 | use ever_client::encoding::decode_abi_number; 6 | use serde::{de, Deserialize, Deserializer}; 7 | use serde_json::{json, Value}; 8 | 9 | pub(super) const ID: &str = "ac1a4d3ecea232e49783df4a23a81823cdca3205dc58cd20c4db259c25605b48"; 10 | 11 | const ABI: &str = r#" 12 | { 13 | "ABI version": 2, 14 | "version": "2.2", 15 | "header": ["time"], 16 | "functions": [ 17 | { 18 | "name": "select", 19 | "id": "0x69814639", 20 | "inputs": [ 21 | {"name":"title","type":"string"}, 22 | {"name":"description","type":"string"}, 23 | {"components":[{"name":"title","type":"string"},{"name":"description","type":"string"},{"name":"handlerId","type":"uint32"}],"name":"items","type":"tuple[]"} 24 | ], 25 | "outputs": [ 26 | {"name":"index","type":"uint32"} 27 | ] 28 | }, 29 | { 30 | "name": "constructor", 31 | "id": "0x68b55f3f", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | ] 36 | } 37 | ], 38 | "data": [ 39 | ], 40 | "events": [ 41 | ], 42 | "fields": [ 43 | {"name":"_pubkey","type":"uint256"}, 44 | {"name":"_timestamp","type":"uint64"}, 45 | {"name":"_constructorFlag","type":"bool"} 46 | ] 47 | } 48 | "#; 49 | 50 | #[derive(Deserialize, Default)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct MenuItem { 53 | title: String, 54 | description: String, 55 | #[serde(deserialize_with = "from_abi_num")] 56 | pub handler_id: u32, 57 | } 58 | 59 | fn from_abi_num<'de, D>(des: D) -> Result 60 | where 61 | D: Deserializer<'de>, 62 | { 63 | let s: String = Deserialize::deserialize(des)?; 64 | decode_abi_number(&s).map_err(de::Error::custom) 65 | } 66 | 67 | pub struct Menu {} 68 | impl Menu { 69 | pub fn new() -> Self { 70 | Self {} 71 | } 72 | 73 | fn select(&self, args: &Value) -> InterfaceResult { 74 | let menu_items: Vec = serde_json::from_value(args["items"].clone()).unwrap(); 75 | let title = decode_string_arg(args, "title")?; 76 | let description = decode_string_arg(args, "description")?; 77 | if !title.is_empty() { 78 | println!("{}", title); 79 | } 80 | if !description.is_empty() { 81 | println!("{}", description); 82 | } 83 | for (i, menu) in menu_items.iter().enumerate() { 84 | println!("{}) {}", i + 1, menu.title); 85 | if !menu.description.is_empty() { 86 | println!(" {}", menu.description); 87 | } 88 | } 89 | loop { 90 | let res = action_input(menu_items.len()); 91 | if res.is_err() { 92 | println!("{}", res.unwrap_err()); 93 | continue; 94 | } 95 | let (n, _, _) = res.unwrap(); 96 | let menu = menu_items.get(n - 1); 97 | if menu.is_none() { 98 | println!("Invalid menu. Try again."); 99 | continue; 100 | } 101 | 102 | return Ok((menu.unwrap().handler_id, json!({ "index": n - 1 }))); 103 | } 104 | } 105 | } 106 | 107 | #[async_trait::async_trait] 108 | impl DebotInterface for Menu { 109 | fn get_id(&self) -> String { 110 | ID.to_string() 111 | } 112 | 113 | fn get_abi(&self) -> Abi { 114 | Abi::Json(ABI.to_owned()) 115 | } 116 | 117 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 118 | match func { 119 | "select" => self.select(args), 120 | _ => Err(format!("function \"{}\" is not implemented", func)), 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/debot/interfaces/userinfo.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::decode_answer_id; 2 | use crate::config::Config; 3 | use crate::debot::term_signing_box::TerminalSigningBox; 4 | use crate::helpers::TonClient; 5 | use ever_client::abi::Abi; 6 | use ever_client::debot::{DebotInterface, InterfaceResult}; 7 | use serde_json::{json, Value}; 8 | 9 | const ID: &str = "a56115147709ed3437efb89460b94a120b7fe94379c795d1ebb0435a847ee580"; 10 | 11 | const ABI: &str = r#" 12 | { 13 | "ABI version": 2, 14 | "version": "2.2", 15 | "header": ["time"], 16 | "functions": [ 17 | { 18 | "name": "getAccount", 19 | "id": "0x2e4fec08", 20 | "inputs": [ 21 | {"name":"answerId","type":"uint32"} 22 | ], 23 | "outputs": [ 24 | {"name":"value","type":"address"} 25 | ] 26 | }, 27 | { 28 | "name": "getPublicKey", 29 | "id": "0x2c5b2088", 30 | "inputs": [ 31 | {"name":"answerId","type":"uint32"} 32 | ], 33 | "outputs": [ 34 | {"name":"value","type":"uint256"} 35 | ] 36 | }, 37 | { 38 | "name": "getSigningBox", 39 | "id": "0x11f1f7db", 40 | "inputs": [ 41 | {"name":"answerId","type":"uint32"} 42 | ], 43 | "outputs": [ 44 | {"name":"handle","type":"uint32"} 45 | ] 46 | }, 47 | { 48 | "name": "constructor", 49 | "id": "0x68b55f3f", 50 | "inputs": [ 51 | ], 52 | "outputs": [ 53 | ] 54 | } 55 | ], 56 | "data": [ 57 | ], 58 | "events": [ 59 | ], 60 | "fields": [ 61 | {"name":"_pubkey","type":"uint256"}, 62 | {"name":"_timestamp","type":"uint64"}, 63 | {"name":"_constructorFlag","type":"bool"} 64 | ] 65 | } 66 | "#; 67 | 68 | pub struct UserInfo { 69 | client: TonClient, 70 | config: Config, 71 | } 72 | impl UserInfo { 73 | pub fn new(client: TonClient, config: Config) -> Self { 74 | Self { client, config } 75 | } 76 | 77 | fn get_account(&self, args: &Value) -> InterfaceResult { 78 | let answer_id = decode_answer_id(args)?; 79 | let value = self 80 | .config 81 | .wallet 82 | .clone() 83 | .unwrap_or_else(|| format!("0:{:064}", 0)); 84 | Ok((answer_id, json!({ "value": value }))) 85 | } 86 | 87 | fn get_public_key(&self, args: &Value) -> InterfaceResult { 88 | let answer_id = decode_answer_id(args)?; 89 | let value = self 90 | .config 91 | .pubkey 92 | .clone() 93 | .unwrap_or_else(|| format!("0x{:064}", 0)); 94 | Ok((answer_id, json!({ "value": value }))) 95 | } 96 | 97 | async fn get_signing_box(&self, args: &Value) -> InterfaceResult { 98 | let answer_id = decode_answer_id(args)?; 99 | let mut signing_box = TerminalSigningBox::new_with_keypath( 100 | self.client.clone(), 101 | self.config.keys_path.clone().unwrap_or_default(), 102 | ) 103 | .await?; 104 | let handle = signing_box.leak(); 105 | Ok((answer_id, json!({ "handle": handle.0}))) 106 | } 107 | } 108 | 109 | #[async_trait::async_trait] 110 | impl DebotInterface for UserInfo { 111 | fn get_id(&self) -> String { 112 | ID.to_string() 113 | } 114 | 115 | fn get_abi(&self) -> Abi { 116 | Abi::Json(ABI.to_owned()) 117 | } 118 | 119 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 120 | match func { 121 | "getAccount" => self.get_account(args), 122 | "getPublicKey" => self.get_public_key(args), 123 | "getSigningBox" => self.get_signing_box(args).await, 124 | _ => Err(format!("function \"{}\" is not implemented", func)), 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/debot/term_encryption_box.rs: -------------------------------------------------------------------------------- 1 | use super::term_browser::input; 2 | use crate::crypto::load_keypair; 3 | use crate::helpers::{TonClient, HD_PATH}; 4 | use ever_client::crypto::{ 5 | register_encryption_box, remove_encryption_box, ChaCha20EncryptionBox, ChaCha20ParamsEB, 6 | EncryptionBoxHandle, NaclBoxParamsEB, NaclEncryptionBox, NaclSecretBoxParamsEB, 7 | NaclSecretEncryptionBox, RegisteredEncryptionBox, 8 | }; 9 | use std::io::{self}; 10 | 11 | #[derive(Clone, Copy)] 12 | pub(crate) enum EncryptionBoxType { 13 | SecretNaCl, 14 | NaCl, 15 | ChaCha20, 16 | } 17 | 18 | pub(crate) struct ParamsOfTerminalEncryptionBox { 19 | pub box_type: EncryptionBoxType, 20 | pub their_pubkey: String, 21 | pub nonce: String, 22 | pub context: TonClient, 23 | } 24 | 25 | pub(super) struct TerminalEncryptionBox { 26 | pub handle: EncryptionBoxHandle, 27 | pub client: TonClient, 28 | } 29 | 30 | impl Drop for TerminalEncryptionBox { 31 | fn drop(&mut self) { 32 | if self.handle.0 != 0 { 33 | let _ = remove_encryption_box( 34 | self.client.clone(), 35 | RegisteredEncryptionBox { 36 | handle: self.handle(), 37 | }, 38 | ); 39 | } 40 | } 41 | } 42 | 43 | impl TerminalEncryptionBox { 44 | pub async fn new(params: ParamsOfTerminalEncryptionBox) -> Result { 45 | let key: String; 46 | 47 | { 48 | let stdio = io::stdin(); 49 | let mut reader = stdio.lock(); 50 | let mut writer = io::stdout(); 51 | let enter_str = "enter seed phrase or path to keypair file"; 52 | let value = input(enter_str, &mut reader, &mut writer); 53 | let pair = load_keypair(&value)?; 54 | key = format!("{:064}", pair.secret); 55 | } 56 | 57 | let registered_box = match params.box_type { 58 | EncryptionBoxType::SecretNaCl => { 59 | register_encryption_box( 60 | params.context.clone(), 61 | NaclSecretEncryptionBox::new( 62 | NaclSecretBoxParamsEB { 63 | key, 64 | nonce: params.nonce, 65 | }, 66 | Some(HD_PATH.to_owned()), 67 | ), 68 | ) 69 | .await 70 | .map_err(|e| e.to_string())? 71 | .handle 72 | } 73 | EncryptionBoxType::NaCl => { 74 | register_encryption_box( 75 | params.context.clone(), 76 | NaclEncryptionBox::new( 77 | NaclBoxParamsEB { 78 | their_public: params.their_pubkey, 79 | secret: key, 80 | nonce: params.nonce, 81 | }, 82 | Some(HD_PATH.to_owned()), 83 | ), 84 | ) 85 | .await 86 | .map_err(|e| e.to_string())? 87 | .handle 88 | } 89 | EncryptionBoxType::ChaCha20 => { 90 | register_encryption_box( 91 | params.context.clone(), 92 | ChaCha20EncryptionBox::new( 93 | ChaCha20ParamsEB { 94 | key, 95 | nonce: params.nonce, 96 | }, 97 | Some(HD_PATH.to_owned()), 98 | ) 99 | .map_err(|e| e.to_string())?, 100 | ) 101 | .await 102 | .map_err(|e| e.to_string())? 103 | .handle 104 | } 105 | }; 106 | Ok(Self { 107 | handle: registered_box, 108 | client: params.context.clone(), 109 | }) 110 | } 111 | pub fn handle(&self) -> EncryptionBoxHandle { 112 | self.handle.clone() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/samples/sample3.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.47.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | // import required DeBot interfaces and basic DeBot contract. 6 | import "Debot.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 8 | import "Shttps://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Sdk/Sdk.sol"; 9 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/EncryptionBoxInput/EncryptionBoxInput.sol"; 10 | 11 | contract EncryptionBoxInputDebot is Debot { 12 | 13 | uint256 constant THEIR_ENC_PUBKEY = 0x2904a07c359c317ad34301e825f6184b29f14b5c6b733155e0aeb62b8bfbee04; 14 | 15 | bytes m_nonce; 16 | uint32 m_handle; 17 | bytes m_data; 18 | 19 | 20 | function start() public override { 21 | m_data = "Hello world!"; 22 | Sdk.genRandom(tvm.functionId(getRandom), 12); 23 | } 24 | 25 | function getRandom(bytes buffer) public { 26 | m_nonce = buffer; 27 | Terminal.print(tvm.functionId(runEncryptionBoxInput),"run EncryptionBoxInput"); 28 | } 29 | 30 | function runEncryptionBoxInput() public { 31 | EncryptionBoxInput.getChaCha20Box(tvm.functionId(chachaBoxHandle),"run chacha",m_nonce); 32 | } 33 | 34 | function chachaBoxHandle(uint32 handle) public { 35 | m_handle = handle; 36 | Sdk.encrypt(tvm.functionId(decrypt), m_handle, m_data); 37 | } 38 | 39 | function decrypt(uint32 result, bytes encrypted) public { 40 | Sdk.decrypt(tvm.functionId(checkResult), m_handle, encrypted); 41 | } 42 | 43 | function checkResult(uint32 result, bytes decrypted) public { 44 | if(string(decrypted) == string(m_data)) { 45 | Terminal.print(tvm.functionId(regenerate),"ChaCha works"); 46 | } 47 | } 48 | 49 | function regenerate() public { 50 | Sdk.genRandom(tvm.functionId(getSecretNaClBox), 24); 51 | } 52 | 53 | function getSecretNaClBox(bytes buffer) public { 54 | m_nonce = buffer; 55 | EncryptionBoxInput.getNaclSecretBox(tvm.functionId(naclSecretBoxHandle),"run secret naclbox",m_nonce); 56 | } 57 | 58 | function naclSecretBoxHandle(uint32 handle) public { 59 | m_handle = handle; 60 | Sdk.encrypt(tvm.functionId(decryptSecretNaCl), m_handle, m_data); 61 | } 62 | 63 | function decryptSecretNaCl(uint32 result, bytes encrypted) public { 64 | Sdk.decrypt(tvm.functionId(checkSecretNaClResult), m_handle, encrypted); 65 | } 66 | 67 | function checkSecretNaClResult(uint32 result, bytes decrypted) public { 68 | if(string(decrypted) == string(m_data)) { 69 | Terminal.print(tvm.functionId(getNaClBox),"SecretNaCl works"); 70 | } 71 | } 72 | 73 | function getNaClBox() public { 74 | EncryptionBoxInput.getNaclBox(tvm.functionId(naclBoxHandle),"run naclbox",m_nonce,THEIR_ENC_PUBKEY); 75 | } 76 | 77 | function naclBoxHandle(uint32 handle) public { 78 | m_handle = handle; 79 | Sdk.encrypt(tvm.functionId(decryptNaCl), m_handle, m_data); 80 | } 81 | 82 | function decryptNaCl(uint32 result, bytes encrypted) public { 83 | Sdk.decrypt(tvm.functionId(checkNaClResult), m_handle, encrypted); 84 | } 85 | 86 | function checkNaClResult(uint32 result, bytes decrypted) public { 87 | if(string(decrypted) == string(m_data)) { 88 | Terminal.print(0,"NaCl works"); 89 | } 90 | } 91 | 92 | /* 93 | * Implementation of DeBot 94 | */ 95 | function getDebotInfo() public functionID(0xDEB) override view returns( 96 | string name, string version, string publisher, string caption, string author, 97 | address support, string hello, string language, string dabi, bytes icon 98 | ) { 99 | name = "EncryptionBoxInput"; 100 | version = "0.1.2"; 101 | publisher = ""; 102 | caption = "Encryption Box Input."; 103 | author = ""; 104 | support = address.makeAddrStd(0, 0x0); 105 | hello = ""; 106 | language = "en"; 107 | dabi = m_debotAbi.get(); 108 | icon = ""; 109 | } 110 | 111 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 112 | return [ Sdk.ID, Terminal.ID, EncryptionBoxInput.ID]; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /tests/samples/test.code: -------------------------------------------------------------------------------- 1 | .version sol 0.65.0 2 | 3 | .macro constructor 4 | DROP 5 | GETGLOB 2 6 | ISNULL 7 | IFREF { 8 | CALL $c4_to_c7_with_init_storage$ 9 | } 10 | GETGLOB 6 11 | THROWIF 51 12 | ENDS 13 | ACCEPT 14 | CALLREF { 15 | CALL $c7_to_c4$ 16 | } 17 | THROW 0 18 | 19 | .macro test 20 | DROP 21 | GETGLOB 6 22 | THROWIFNOT 76 23 | GETGLOB 2 24 | ISNULL 25 | IFREF { 26 | CALL $c4_to_c7$ 27 | } 28 | .loc test.sol, 12 29 | LDU 256 30 | LDREF 31 | ENDS 32 | .loc test.sol, 0 33 | CALLREF { 34 | CALL $test_ad1c61fd_internal_macro$ 35 | } 36 | CALLREF { 37 | CALL $c7_to_c4$ 38 | } 39 | THROW 0 40 | 41 | .globl test_ad1c61fd_internal 42 | .type test_ad1c61fd_internal, @function 43 | CALL $test_ad1c61fd_internal_macro$ 44 | 45 | .macro test_ad1c61fd_internal_macro 46 | .loc test.sol, 7 47 | DROP2 48 | GETGLOB 5 49 | DUP 50 | ISNULL 51 | PUSHCONT { 52 | DROP 53 | PUSHINT 0 54 | } 55 | IF 56 | GETGLOB 2 57 | EQUAL 58 | THROWIFNOT 102 59 | .loc test.sol, 8 60 | ACCEPT 61 | .loc test.sol, 0 62 | 63 | .macro get 64 | DROP 65 | GETGLOB 6 66 | THROWIFNOT 76 67 | GETGLOB 2 68 | ISNULL 69 | IFREF { 70 | CALL $c4_to_c7$ 71 | } 72 | .loc test.sol, 15 73 | LDU 256 74 | LDREF 75 | ENDS 76 | .loc test.sol, 0 77 | CALLREF { 78 | CALL $get_805da4ad_internal_macro$ 79 | } 80 | OVER 81 | PUSHCONT { 82 | PUSH S3 83 | CTOS 84 | LDU 2 85 | LDMSGADDR 86 | DROP 87 | NIP 88 | NEWC 89 | STSLICECONST xc 90 | STSLICE 91 | PUSHINT 2363537267 92 | STUR 130 93 | STU 256 94 | ENDC 95 | PUSHINT 0 96 | SENDRAWMSG 97 | } 98 | PUSHCONT { 99 | DROP 100 | } 101 | IFELSE 102 | CALLREF { 103 | CALL $c7_to_c4$ 104 | } 105 | THROW 0 106 | 107 | .globl get_805da4ad_internal 108 | .type get_805da4ad_internal, @function 109 | CALL $get_805da4ad_internal_macro$ 110 | 111 | .macro get_805da4ad_internal_macro 112 | .loc test.sol, 16 113 | DROP 114 | INC 115 | .loc test.sol, 0 116 | 117 | .macro c4_to_c7 118 | PUSHROOT 119 | CTOS 120 | LDU 256 ; pubkey c4 121 | LDU 64 ; pubkey timestamp c4 122 | LDU 1 ; ctor flag 123 | NIP 124 | ENDS 125 | SETGLOB 3 126 | SETGLOB 2 127 | 128 | .macro c4_to_c7_with_init_storage 129 | PUSHROOT 130 | CTOS 131 | SBITS 132 | GTINT 1 133 | PUSHCONT { 134 | PUSHINT 0 135 | PUSHROOT 136 | CTOS 137 | PLDDICT ; D 138 | PUSHINT 64 139 | DICTUGET 140 | THROWIFNOT 61 141 | PLDU 256 142 | SETGLOB 2 143 | PUSHINT 0 ; timestamp 144 | SETGLOB 3 145 | } 146 | IFREFELSE { 147 | CALL $c4_to_c7$ 148 | } 149 | 150 | .macro c7_to_c4 151 | GETGLOB 3 152 | GETGLOB 2 153 | NEWC 154 | STU 256 155 | STU 64 156 | STONE 157 | ENDC 158 | POPROOT 159 | 160 | .macro upd_only_time_in_c4 161 | PUSHROOT 162 | CTOS 163 | LDU 256 164 | LDU 64 165 | NIP 166 | GETGLOB 3 167 | ROT 168 | NEWC 169 | STU 256 170 | STU 64 171 | STSLICE 172 | ENDC 173 | POPROOT 174 | 175 | .internal-alias :main_internal, 0 176 | .internal :main_internal 177 | PUSHROOT 178 | CTOS 179 | SBITS 180 | NEQINT 1 181 | SETGLOB 6 182 | PUSH S2 183 | CTOS 184 | PLDU 4 185 | MODPOW2 1 186 | IFRET 187 | OVER 188 | SEMPTY ; isEmpty 189 | IFJMPREF { 190 | GETGLOB 6 191 | THROWIFNOT 76 192 | } 193 | OVER 194 | LDUQ 32 ; [funcId] body' ok 195 | THROWIFNOT 60 196 | OVER 197 | IFNOTJMPREF { 198 | GETGLOB 6 199 | THROWIFNOT 76 200 | } 201 | SWAP 202 | CALLREF { 203 | CALL $public_function_selector$ 204 | } 205 | THROW 60 206 | 207 | .internal-alias :main_external, -1 208 | .internal :main_external 209 | PUSHROOT 210 | CTOS 211 | SBITS 212 | NEQINT 1 213 | SETGLOB 6 214 | OVER 215 | CALLREF { 216 | CALL $c4_to_c7_with_init_storage$ 217 | } 218 | LDU 1 ; haveSign msgSlice 219 | SWAP 220 | PUSHCONT { 221 | PUSHPOW2 9 222 | LDSLICEX 223 | DUP 224 | MYADDR 225 | NEWC 226 | STSLICE 227 | STSLICE 228 | ENDC 229 | HASHCU 230 | ROT 231 | GETGLOB 2 232 | DUP 233 | SETGLOB 5 234 | CHKSIGNU 235 | THROWIFNOT 40 236 | } 237 | IF 238 | LDU 64 ; timestamp msgSlice 239 | SWAP 240 | CALL $replay_protection_macro$ 241 | LDU 32 ; expireAt msgSlice 242 | SWAP 243 | NOW ; msgSlice expireAt now 244 | GREATER ; msgSlice expireAt>now 245 | THROWIFNOT 57 246 | LDU 32 ; funcId body 247 | SWAP 248 | CALLREF { 249 | CALL $public_function_selector$ 250 | } 251 | THROW 60 252 | 253 | .macro public_function_selector 254 | DUP 255 | PUSHINT 216053619 256 | EQUAL 257 | IFJMPREF { 258 | CALL $get$ 259 | } 260 | DUP 261 | PUSHINT 1756716863 262 | EQUAL 263 | IFJMPREF { 264 | CALL $constructor$ 265 | } 266 | DUP 267 | PUSHINT 1967234084 268 | EQUAL 269 | IFJMPREF { 270 | CALL $test$ 271 | } 272 | 273 | -------------------------------------------------------------------------------- /tests/samples/fakeDepool.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.6.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | 5 | interface IFakeDePoolClient { 6 | function receiveAnswer(uint32 errcode, uint64 comment) external; 7 | } 8 | 9 | contract FakeDePool { 10 | 11 | uint static m_seed; 12 | uint64 m_stake; 13 | address m_sender; 14 | address m_receiver; 15 | uint32 m_withdrawal; 16 | uint32 m_total; 17 | bool m_reinvest; 18 | uint128 m_value; 19 | 20 | event StakeSigningRequested(uint32 electionId, address proxy); 21 | 22 | function sendAnswer() public pure { 23 | IFakeDePoolClient(msg.sender).receiveAnswer(11, 222); 24 | } 25 | 26 | function addOrdinaryStake(uint64 stake) public { 27 | m_stake = stake; 28 | m_sender = msg.sender; 29 | m_value = msg.value; 30 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(11, 222); 31 | } 32 | 33 | function addVestingStake(uint64 stake, address beneficiary, uint32 withdrawalPeriod, uint32 totalPeriod) public { 34 | m_sender = msg.sender; 35 | m_value = msg.value; 36 | m_stake = stake; 37 | m_receiver = beneficiary; 38 | m_withdrawal = withdrawalPeriod; 39 | m_total = totalPeriod; 40 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 41 | } 42 | 43 | function addLockStake(uint64 stake, address beneficiary, uint32 withdrawalPeriod, uint32 totalPeriod) public { 44 | m_sender = msg.sender; 45 | m_value = msg.value; 46 | m_stake = stake; 47 | m_receiver = beneficiary; 48 | m_withdrawal = withdrawalPeriod; 49 | m_total = totalPeriod; 50 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 51 | } 52 | 53 | function withdrawFromPoolingRound(uint64 withdrawValue) public { 54 | m_sender = msg.sender; 55 | m_value = msg.value; 56 | m_stake = withdrawValue; 57 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 58 | } 59 | 60 | function withdrawPart(uint64 withdrawValue) public { 61 | m_sender = msg.sender; 62 | m_value = msg.value; 63 | m_stake = withdrawValue; 64 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 65 | } 66 | 67 | function withdrawAll() public { 68 | m_sender = msg.sender; 69 | m_value = msg.value; 70 | m_reinvest = false; 71 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 72 | } 73 | 74 | function cancelWithdrawal() public { 75 | m_sender = msg.sender; 76 | m_value = msg.value; 77 | m_reinvest = true; 78 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 79 | } 80 | 81 | function transferStake(address dest, uint64 amount) public { 82 | m_sender = msg.sender; 83 | m_value = msg.value; 84 | m_stake = amount; 85 | m_receiver = dest; 86 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 87 | } 88 | 89 | function ticktock() public { 90 | m_sender = msg.sender; 91 | m_value = msg.value; 92 | msg.sender.transfer({value: 123, flag: 1}); 93 | 94 | emit StakeSigningRequested(1, address(2)); 95 | } 96 | 97 | function receiveFunds() public { 98 | m_sender = msg.sender; 99 | m_value = msg.value; 100 | } 101 | 102 | function getData() public view returns (uint64 stake, address sender, address receiver, 103 | uint32 withdrawal, uint32 total, bool reinvest, uint128 value) { 104 | return (m_stake, m_sender, m_receiver, m_withdrawal, m_total, m_reinvest, m_value); 105 | } 106 | 107 | function setVestingDonor(address donor) public { 108 | m_receiver = donor; 109 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 110 | } 111 | 112 | function setLockDonor(address donor) public { 113 | m_receiver = donor; 114 | IFakeDePoolClient(msg.sender).receiveAnswer{value: 123456789}(0, 0); 115 | } 116 | 117 | function error(uint code) public pure { 118 | revert(code); 119 | } 120 | 121 | function outOfGas() public pure { 122 | mapping(uint => uint) map; 123 | uint k = 0; 124 | while (k <= 9999) { 125 | map[k] = k; 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /tests/samples/SetcodeMultisigWallet.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "constructor", 7 | "inputs": [ 8 | {"name":"owners","type":"uint256[]"}, 9 | {"name":"reqConfirms","type":"uint8"} 10 | ], 11 | "outputs": [ 12 | ] 13 | }, 14 | { 15 | "name": "acceptTransfer", 16 | "inputs": [ 17 | {"name":"payload","type":"bytes"} 18 | ], 19 | "outputs": [ 20 | ] 21 | }, 22 | { 23 | "name": "sendTransaction", 24 | "inputs": [ 25 | {"name":"dest","type":"address"}, 26 | {"name":"value","type":"uint128"}, 27 | {"name":"bounce","type":"bool"}, 28 | {"name":"flags","type":"uint8"}, 29 | {"name":"payload","type":"cell"} 30 | ], 31 | "outputs": [ 32 | ] 33 | }, 34 | { 35 | "name": "submitTransaction", 36 | "inputs": [ 37 | {"name":"dest","type":"address"}, 38 | {"name":"value","type":"uint128"}, 39 | {"name":"bounce","type":"bool"}, 40 | {"name":"allBalance","type":"bool"}, 41 | {"name":"payload","type":"cell"} 42 | ], 43 | "outputs": [ 44 | {"name":"transId","type":"uint64"} 45 | ] 46 | }, 47 | { 48 | "name": "confirmTransaction", 49 | "inputs": [ 50 | {"name":"transactionId","type":"uint64"} 51 | ], 52 | "outputs": [ 53 | ] 54 | }, 55 | { 56 | "name": "isConfirmed", 57 | "inputs": [ 58 | {"name":"mask","type":"uint32"}, 59 | {"name":"index","type":"uint8"} 60 | ], 61 | "outputs": [ 62 | {"name":"confirmed","type":"bool"} 63 | ] 64 | }, 65 | { 66 | "name": "getParameters", 67 | "inputs": [ 68 | ], 69 | "outputs": [ 70 | {"name":"maxQueuedTransactions","type":"uint8"}, 71 | {"name":"maxCustodianCount","type":"uint8"}, 72 | {"name":"expirationTime","type":"uint64"}, 73 | {"name":"minValue","type":"uint128"}, 74 | {"name":"requiredTxnConfirms","type":"uint8"}, 75 | {"name":"requiredUpdConfirms","type":"uint8"} 76 | ] 77 | }, 78 | { 79 | "name": "getTransaction", 80 | "inputs": [ 81 | {"name":"transactionId","type":"uint64"} 82 | ], 83 | "outputs": [ 84 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"}],"name":"trans","type":"tuple"} 85 | ] 86 | }, 87 | { 88 | "name": "getTransactions", 89 | "inputs": [ 90 | ], 91 | "outputs": [ 92 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"}],"name":"transactions","type":"tuple[]"} 93 | ] 94 | }, 95 | { 96 | "name": "getTransactionIds", 97 | "inputs": [ 98 | ], 99 | "outputs": [ 100 | {"name":"ids","type":"uint64[]"} 101 | ] 102 | }, 103 | { 104 | "name": "getCustodians", 105 | "inputs": [ 106 | ], 107 | "outputs": [ 108 | {"components":[{"name":"index","type":"uint8"},{"name":"pubkey","type":"uint256"}],"name":"custodians","type":"tuple[]"} 109 | ] 110 | }, 111 | { 112 | "name": "submitUpdate", 113 | "inputs": [ 114 | {"name":"codeHash","type":"uint256"}, 115 | {"name":"owners","type":"uint256[]"}, 116 | {"name":"reqConfirms","type":"uint8"} 117 | ], 118 | "outputs": [ 119 | {"name":"updateId","type":"uint64"} 120 | ] 121 | }, 122 | { 123 | "name": "confirmUpdate", 124 | "inputs": [ 125 | {"name":"updateId","type":"uint64"} 126 | ], 127 | "outputs": [ 128 | ] 129 | }, 130 | { 131 | "name": "executeUpdate", 132 | "inputs": [ 133 | {"name":"updateId","type":"uint64"}, 134 | {"name":"code","type":"cell"} 135 | ], 136 | "outputs": [ 137 | ] 138 | }, 139 | { 140 | "name": "getUpdateRequests", 141 | "inputs": [ 142 | ], 143 | "outputs": [ 144 | {"components":[{"name":"id","type":"uint64"},{"name":"index","type":"uint8"},{"name":"signs","type":"uint8"},{"name":"confirmationsMask","type":"uint32"},{"name":"creator","type":"uint256"},{"name":"codeHash","type":"uint256"},{"name":"custodians","type":"uint256[]"},{"name":"reqConfirms","type":"uint8"}],"name":"updates","type":"tuple[]"} 145 | ] 146 | } 147 | ], 148 | "data": [ 149 | ], 150 | "events": [ 151 | { 152 | "name": "TransferAccepted", 153 | "inputs": [ 154 | {"name":"payload","type":"bytes"} 155 | ], 156 | "outputs": [ 157 | ] 158 | } 159 | ] 160 | } 161 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use lazy_static::*; 3 | use predicates::prelude::*; 4 | use serde_json::{Map, Value}; 5 | use std::env; 6 | 7 | #[allow(dead_code)] 8 | pub mod create { 9 | use super::*; 10 | 11 | pub const BIN_NAME: &str = "ever-cli"; 12 | pub const GIVER_ADDR: &str = 13 | "0:841288ed3b55d9cdafa806807f02a0ae0c169aa5edfe88a789a6482429756a94"; 14 | pub const GIVER_ABI: &str = "tests/samples/giver.abi.json"; 15 | pub const GIVER_V2_ADDR: &str = 16 | "0:ece57bcc6c530283becbbd8a3b24d3c5987cdddc3c8b7b33be6e4a6312490415"; 17 | pub const GIVER_V2_ABI: &str = "tests/samples/giver_v2.abi.json"; 18 | pub const GIVER_V2_KEY: &str = "tests/samples/giver_v2.key"; 19 | 20 | lazy_static! { 21 | pub static ref NETWORK: String = 22 | env::var("TON_NETWORK_ADDRESS").unwrap_or("http://127.0.0.1/".to_string()); 23 | } 24 | 25 | pub fn get_config() -> Result, Box> { 26 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 27 | let out = cmd 28 | .arg("config") 29 | .arg("--list") 30 | .output() 31 | .expect("Failed to get config."); 32 | 33 | let mut out = String::from_utf8_lossy(&out.stdout).to_string(); 34 | out.replace_range(..out.find('\n').unwrap_or(0), ""); 35 | let parsed: Value = serde_json::from_str(&out)?; 36 | let obj: Map = parsed.as_object().unwrap().clone(); 37 | Ok(obj) 38 | } 39 | 40 | pub fn set_config( 41 | config: &[&str], 42 | argument: &[&str], 43 | config_path: Option<&str>, 44 | ) -> Result<(), Box> { 45 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 46 | if config_path.is_some() { 47 | cmd.arg("--config").arg(config_path.unwrap()); 48 | } 49 | cmd.arg("config"); 50 | for i in 0..config.len() { 51 | cmd.arg(config[i]).arg(argument[i]); 52 | } 53 | cmd.assert() 54 | .success() 55 | .stdout(predicate::str::contains("Succeeded")); 56 | Ok(()) 57 | } 58 | 59 | pub fn giver(addr: &str) { 60 | let mut cmd = Command::cargo_bin(BIN_NAME).unwrap(); 61 | cmd.arg("call") 62 | .arg("--abi") 63 | .arg(GIVER_ABI) 64 | .arg(GIVER_ADDR) 65 | .arg("sendGrams") 66 | .arg(format!(r#"{{"dest":"{}","amount":1000000000}}"#, addr)); 67 | cmd.assert().success(); 68 | } 69 | 70 | pub fn giver_v2(addr: &str) { 71 | let mut cmd = Command::cargo_bin(BIN_NAME).unwrap(); 72 | cmd.arg("call") 73 | .arg("--abi") 74 | .arg(GIVER_V2_ABI) 75 | .arg(GIVER_V2_ADDR) 76 | .arg("--sign") 77 | .arg(GIVER_V2_KEY) 78 | .arg("sendTransaction") 79 | .arg(format!( 80 | r#"{{"dest":"{}","value":100000000000,"bounce":false}}"#, 81 | addr 82 | )); 83 | cmd.assert().success(); 84 | } 85 | 86 | pub fn grep_address(output: &[u8]) -> String { 87 | let mut addr = String::from_utf8_lossy(output).to_string(); 88 | addr.replace_range(..addr.find("0:").unwrap_or(0), ""); 89 | addr.replace_range(addr.find("testnet").unwrap_or(addr.len()) - 1.., ""); 90 | addr 91 | } 92 | 93 | pub fn grep_message_id(output: &[u8]) -> String { 94 | let mut message_id = String::from_utf8_lossy(output).to_string(); 95 | let index = message_id 96 | .find("MessageId: ") 97 | .map(|i| i + "MessageId: ".len()) 98 | .unwrap_or(0); 99 | message_id.replace_range(..index, ""); 100 | if message_id.len() >= 64 { 101 | message_id.replace_range(64.., ""); 102 | } 103 | message_id 104 | } 105 | 106 | pub fn generate_key_and_address( 107 | key_path: &str, 108 | tvc_path: &str, 109 | abi_path: &str, 110 | ) -> Result> { 111 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 112 | let out = cmd 113 | .arg("genaddr") 114 | .arg("--genkey") 115 | .arg(key_path) 116 | .arg(tvc_path) 117 | .arg("--abi") 118 | .arg(abi_path) 119 | .output() 120 | .expect("Failed to generate address."); 121 | 122 | Ok(grep_address(&out.stdout)) 123 | } 124 | 125 | pub fn generate_phrase_and_key(key_path: &str) -> Result> { 126 | let mut cmd = Command::cargo_bin(BIN_NAME)?; 127 | let out = cmd 128 | .arg("genphrase") 129 | .arg("--dump") 130 | .arg(key_path) 131 | .output() 132 | .expect("Failed to generate a seed phrase."); 133 | let mut seed = String::from_utf8_lossy(&out.stdout).to_string(); 134 | seed.replace_range(..seed.find('"').unwrap_or(0) + 1, ""); 135 | seed.replace_range(seed.find("Keypair").unwrap_or(seed.len()) - 2.., ""); 136 | 137 | Ok(seed) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/samples/PipechainTest.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.47.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "https://raw.githubusercontent.com/everx-labs/debots/main/Debot.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/ConfirmInput/ConfirmInput.sol"; 8 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Menu/Menu.sol"; 9 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/AddressInput/AddressInput.sol"; 10 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/AmountInput/AmountInput.sol"; 11 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/UserInfo/UserInfo.sol"; 12 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/SigningBoxInput/SigningBoxInput.sol"; 13 | import "ICompleted.sol"; 14 | 15 | contract Invoked is Debot { 16 | 17 | bytes m_icon; 18 | address m_invoker; 19 | uint64 m_arg1; 20 | string m_arg2; 21 | bool m_arg3; 22 | uint32 m_arg4; 23 | address m_arg5; 24 | uint256 m_arg6; 25 | mapping(uint32 => Data) m_arg7; 26 | 27 | function setIcon(bytes icon) public { 28 | require(msg.pubkey() == tvm.pubkey(), 100); 29 | tvm.accept(); 30 | m_icon = icon; 31 | } 32 | 33 | /// @notice Returns Metadata about DeBot. 34 | function getDebotInfo() public functionID(0xDEB) override view returns( 35 | string name, string version, string publisher, string caption, string author, 36 | address support, string hello, string language, string dabi, bytes icon 37 | ) { 38 | name = "InvokeTest"; 39 | version = "0.1.0"; 40 | publisher = "EverX"; 41 | caption = "For testing invokes"; 42 | author = "EverX"; 43 | support = address(0); 44 | hello = "Hello"; 45 | language = "en"; 46 | dabi = m_debotAbi.get(); 47 | icon = m_icon; 48 | } 49 | 50 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 51 | return [Terminal.ID, AddressInput.ID, AmountInput.ID, ConfirmInput.ID, Menu.ID, UserInfo.ID]; 52 | } 53 | 54 | /// @notice Entry point function for DeBot. 55 | function start() public override { 56 | uint256[] empty; 57 | SigningBoxInput.get(tvm.functionId(setSignBoxHandle), "Enter keys", empty); 58 | } 59 | 60 | function setSignBoxHandle(uint32 handle) public { 61 | require(handle != 0); 62 | this.setDataOnchain{ 63 | abiVer: 2, extMsg: true, sign: true, 64 | time: 0, expire: 0, pubkey: 0, signBoxHandle: handle, 65 | callbackId: tvm.functionId(onSuccess), 66 | onErrorId: tvm.functionId(onError) 67 | }(); 68 | } 69 | 70 | function onSuccess() public { 71 | 72 | } 73 | 74 | function onError(uint32 sdkError, uint32 exitCode) public { 75 | sdkError; exitCode; 76 | revert(); 77 | } 78 | 79 | function setDataOnchain() public { 80 | require(msg.pubkey() == tvm.pubkey()); 81 | tvm.accept(); 82 | } 83 | 84 | // 85 | // Invoke funcitons 86 | // 87 | 88 | function invokeTest(uint64 arg1, string arg2, bool arg3, uint32 arg4, address arg5, uint256 arg6, mapping(uint32 => Data) arg7) public { 89 | m_invoker = msg.sender; 90 | AmountInput.get(tvm.functionId(checkArg1), "Enter arg1 as amount:", 9, 0, 100 ton); 91 | Terminal.input(tvm.functionId(checkArg2), "Enter arg2 as string:", false); 92 | Terminal.print(0, "Print smthg"); 93 | ConfirmInput.get(tvm.functionId(checkArg3), "Enter arg3 as boolean:"); 94 | MenuItem[] items; 95 | for(uint32 i = 0; i < arg4 + 1; i++) { 96 | items.push(MenuItem(format("Item {}", i), "", tvm.functionId(checkArg4)) ); 97 | } 98 | Menu.select("Enter arg4 as menu index:", "", items); 99 | AddressInput.get(tvm.functionId(checkArg5), "Enter arg5 as address:"); 100 | UserInfo.getPublicKey(tvm.functionId(checkArg6)); 101 | m_arg1 = arg1; 102 | m_arg2 = arg2; 103 | m_arg3 = arg3; 104 | m_arg4 = arg4; 105 | m_arg5 = arg5; 106 | m_arg6 = arg6; 107 | m_arg7 = arg7; 108 | } 109 | 110 | // ---------------------------------------------------- 111 | 112 | function checkArg1(uint128 value) public { 113 | require(value == m_arg1, 201); 114 | } 115 | 116 | function checkArg2(string value) public { 117 | require(value == m_arg2, 202); 118 | } 119 | 120 | function checkArg3(bool value) public { 121 | require(value == m_arg3, 203); 122 | } 123 | 124 | function checkArg4(uint32 index) public { 125 | require(index == m_arg4, 204); 126 | } 127 | 128 | function checkArg5(address value) public { 129 | require(value == m_arg5, 205); 130 | } 131 | 132 | function checkArg6(uint256 value) public { 133 | require(value == m_arg6, 206); 134 | IOnInvokeCompleted(m_invoker).OnInvokeCompleted(Status.Passed, m_arg7); 135 | } 136 | } -------------------------------------------------------------------------------- /tests/samples/PipechainTest_2.sol: -------------------------------------------------------------------------------- 1 | pragma ton-solidity >=0.47.0; 2 | pragma AbiHeader expire; 3 | pragma AbiHeader time; 4 | pragma AbiHeader pubkey; 5 | import "https://raw.githubusercontent.com/everx-labs/debots/main/Debot.sol"; 6 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Terminal/Terminal.sol"; 7 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/ConfirmInput/ConfirmInput.sol"; 8 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/Menu/Menu.sol"; 9 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/AddressInput/AddressInput.sol"; 10 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/AmountInput/AmountInput.sol"; 11 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/UserInfo/UserInfo.sol"; 12 | import "https://raw.githubusercontent.com/everx-labs/DeBot-IS-consortium/main/SigningBoxInput/SigningBoxInput.sol"; 13 | import "ICompleted.sol"; 14 | 15 | contract Invoked is Debot { 16 | 17 | bytes m_icon; 18 | address m_invoker; 19 | uint64 m_arg1; 20 | string m_arg2; 21 | bool m_arg3; 22 | uint32 m_arg4; 23 | address m_arg5; 24 | uint256 m_arg6; 25 | mapping(uint32 => Data) m_arg7; 26 | 27 | function setIcon(bytes icon) public { 28 | require(msg.pubkey() == tvm.pubkey(), 100); 29 | tvm.accept(); 30 | m_icon = icon; 31 | } 32 | 33 | /// @notice Returns Metadata about DeBot. 34 | function getDebotInfo() public functionID(0xDEB) override view returns( 35 | string name, string version, string publisher, string caption, string author, 36 | address support, string hello, string language, string dabi, bytes icon 37 | ) { 38 | name = "InvokeTest"; 39 | version = "0.1.0"; 40 | publisher = "EverX"; 41 | caption = "For testing invokes"; 42 | author = "EverX"; 43 | support = address(0); 44 | hello = "Hello"; 45 | language = "en"; 46 | dabi = m_debotAbi.get(); 47 | icon = m_icon; 48 | } 49 | 50 | function getRequiredInterfaces() public view override returns (uint256[] interfaces) { 51 | return [Terminal.ID, AddressInput.ID, AmountInput.ID, ConfirmInput.ID, Menu.ID, UserInfo.ID]; 52 | } 53 | 54 | /// @notice Entry point function for DeBot. 55 | function start() public override { 56 | uint256[] empty; 57 | SigningBoxInput.get(tvm.functionId(setSignBoxHandle), "Enter keys", empty); 58 | } 59 | 60 | function setSignBoxHandle(uint32 handle) public { 61 | require(handle != 0); 62 | this.setDataOnchain{ 63 | abiVer: 2, extMsg: true, sign: true, 64 | time: 0, expire: 0, pubkey: 0, signBoxHandle: handle, 65 | callbackId: tvm.functionId(onSuccess), 66 | onErrorId: tvm.functionId(onError) 67 | }(); 68 | } 69 | 70 | function onSuccess() public { 71 | 72 | } 73 | 74 | function onError(uint32 sdkError, uint32 exitCode) public { 75 | sdkError; exitCode; 76 | revert(); 77 | } 78 | 79 | function setDataOnchain() public { 80 | require(msg.pubkey() == tvm.pubkey()); 81 | tvm.accept(); 82 | } 83 | 84 | // 85 | // Invoke funcitons 86 | // 87 | 88 | function invokeTest(uint64 arg1, string arg2, bool arg3, uint32 arg4, address arg5, uint256 arg6, mapping(uint32 => Data) arg7) public { 89 | m_invoker = msg.sender; 90 | AmountInput.get(tvm.functionId(checkArg1), "Enter arg1 as amount:", 9, 0, 100 ton); 91 | Terminal.input(tvm.functionId(checkArg2), "Enter arg2 as string:", false); 92 | Terminal.print(0, "Print smthg"); 93 | ConfirmInput.get(tvm.functionId(checkArg3), "Enter arg3 as boolean:"); 94 | MenuItem[] items; 95 | for(uint32 i = 0; i < arg4 + 1; i++) { 96 | items.push(MenuItem(format("Item {}", i), "", tvm.functionId(checkArg4)) ); 97 | } 98 | Menu.select("Enter arg4 as menu index:", "", items); 99 | AddressInput.get(tvm.functionId(checkArg5), "Enter arg5 as address:"); 100 | UserInfo.getPublicKey(tvm.functionId(checkArg6)); 101 | m_arg1 = arg1; 102 | m_arg2 = arg2; 103 | m_arg3 = arg3; 104 | m_arg4 = arg4; 105 | m_arg5 = arg5; 106 | m_arg6 = arg6; 107 | m_arg7 = arg7; 108 | } 109 | 110 | // ---------------------------------------------------- 111 | 112 | function checkArg1(uint128 value) public { 113 | require(value == m_arg1, 201); 114 | } 115 | 116 | function checkArg2(string value) public { 117 | require(value == m_arg2, 202); 118 | } 119 | 120 | function checkArg3(bool value) public { 121 | require(value == m_arg3, 203); 122 | } 123 | 124 | function checkArg4(uint32 index) public { 125 | require(index == m_arg4, 204); 126 | } 127 | 128 | function checkArg5(address value) public { 129 | require(value == m_arg5, 205); 130 | } 131 | 132 | function checkArg6(uint256 value) public { 133 | require(value == m_arg6, 206); 134 | IOnInvokeCompleted(m_invoker).OnInvokeCompleted(Status.Passed, m_arg7); 135 | } 136 | } -------------------------------------------------------------------------------- /src/debot/processor.rs: -------------------------------------------------------------------------------- 1 | use super::{ApproveKind, ChainLink, PipeChain}; 2 | use ever_client::abi::{Abi, CallSet}; 3 | use ever_client::debot::DebotActivity; 4 | use serde_json::Value; 5 | use std::vec::IntoIter; 6 | 7 | #[derive(Debug)] 8 | pub enum ProcessorError { 9 | InterfaceCallNeeded, 10 | NoMoreChainlinks, 11 | UnexpectedChainLinkKind, 12 | UnexpectedInterface, 13 | UnexpectedMethod, 14 | InteractiveApproveNeeded, 15 | // TODO: 16 | // UnexpectedApproveKind, 17 | } 18 | 19 | pub struct ChainProcessor { 20 | pipechain: PipeChain, 21 | chain_iter: IntoIter, 22 | } 23 | 24 | impl ChainProcessor { 25 | pub fn new(mut pipechain: PipeChain) -> Self { 26 | let chain_vec = std::mem::take(&mut pipechain.chain); 27 | Self { 28 | pipechain, 29 | chain_iter: chain_vec.into_iter(), 30 | } 31 | } 32 | 33 | pub fn abi(&self) -> Option { 34 | self.pipechain.abi.clone().map(|v| Abi::Json(v.to_string())) 35 | } 36 | 37 | pub fn interactive(&self) -> bool { 38 | !self.pipechain.quiet 39 | } 40 | 41 | pub fn default_start(&self) -> bool { 42 | self.pipechain.init_method == "start" 43 | } 44 | 45 | pub fn print(&self, message: &str) { 46 | if self.interactive() { 47 | println!("{}", message); 48 | } 49 | } 50 | 51 | pub fn initial_msg(&self) -> Option { 52 | self.pipechain.init_msg.clone() 53 | } 54 | 55 | pub fn initial_call_set(&self) -> Option { 56 | if self.pipechain.init_msg.is_some() { 57 | return None; 58 | } 59 | if self.default_start() { 60 | return None; 61 | } 62 | match &self.pipechain.init_args { 63 | Some(args) => { 64 | CallSet::some_with_function_and_input(&self.pipechain.init_method, args.clone()) 65 | } 66 | None => CallSet::some_with_function(&self.pipechain.init_method), 67 | } 68 | } 69 | 70 | pub fn next_input( 71 | &mut self, 72 | in_interface: &str, 73 | in_method: &str, 74 | _in_params: &Value, 75 | ) -> Result, ProcessorError> { 76 | let chlink = self.chain_iter.next().ok_or(if self.interactive() { 77 | ProcessorError::InterfaceCallNeeded 78 | } else { 79 | ProcessorError::NoMoreChainlinks 80 | })?; 81 | 82 | match chlink { 83 | ChainLink::Input { 84 | interface, 85 | method, 86 | params, 87 | mandatory, 88 | } => { 89 | if interface != in_interface { 90 | if !mandatory { 91 | self.next_input(in_interface, in_method, _in_params) 92 | } else { 93 | Err(ProcessorError::UnexpectedInterface) 94 | } 95 | } else if method != in_method { 96 | Err(ProcessorError::UnexpectedMethod) 97 | } else { 98 | Ok(params) 99 | } 100 | } 101 | _ => Err(ProcessorError::UnexpectedChainLinkKind), 102 | } 103 | } 104 | 105 | pub fn next_signing_box(&mut self) -> Result { 106 | let chlink = self.chain_iter.next().ok_or(if self.interactive() { 107 | ProcessorError::InterfaceCallNeeded 108 | } else { 109 | ProcessorError::NoMoreChainlinks 110 | })?; 111 | 112 | match chlink { 113 | ChainLink::SigningBox { handle } => Ok(handle), 114 | _ => Err(ProcessorError::UnexpectedChainLinkKind), 115 | } 116 | } 117 | 118 | pub fn next_approve(&mut self, activity: &DebotActivity) -> Result { 119 | let app_kind = match activity { 120 | DebotActivity::Transaction { .. } => ApproveKind::ApproveOnChainCall, 121 | }; 122 | let auto_approve = self 123 | .pipechain 124 | .auto_approve 125 | .as_ref() 126 | .map(|vec| vec.iter().any(|x| *x == app_kind)); 127 | 128 | let chlink = self.chain_iter.next(); 129 | if chlink.is_none() { 130 | if let Some(auto_approve) = auto_approve { 131 | return Ok(auto_approve); 132 | } else if self.interactive() { 133 | return Err(ProcessorError::InteractiveApproveNeeded); 134 | } else { 135 | return Ok(false); 136 | } 137 | } 138 | 139 | // TODO: ? 140 | let chlink = chlink.unwrap(); 141 | match chlink { 142 | ChainLink::OnchainCall { 143 | approve, 144 | iflq: _, 145 | ifeq: _, 146 | } => match activity { 147 | DebotActivity::Transaction { 148 | msg: _, 149 | dst: _, 150 | out: _, 151 | fee: _, 152 | setcode: _, 153 | signkey: _, 154 | signing_box_handle: _, 155 | } => Ok(approve), 156 | }, 157 | _ => Err(ProcessorError::UnexpectedChainLinkKind), 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /tests/samples/arguments.code: -------------------------------------------------------------------------------- 1 | .version sol 0.63.0 2 | 3 | .macro constructor 4 | DROP 5 | GETGLOB 2 6 | ISNULL 7 | IFREF { 8 | CALL $c4_to_c7_with_init_storage$ 9 | } 10 | GETGLOB 6 11 | THROWIF 51 12 | ENDS 13 | ACCEPT 14 | CALLREF { 15 | CALL $c7_to_c4$ 16 | } 17 | THROW 0 18 | 19 | .macro add 20 | DROP 21 | GETGLOB 6 22 | THROWIFNOT 76 23 | GETGLOB 2 24 | ISNULL 25 | IFREF { 26 | CALL $c4_to_c7$ 27 | } 28 | .loc arguments.sol, 7 29 | OVER 30 | PUSHCONT { 31 | LDU 256 32 | LDREF 33 | ENDS 34 | CTOS 35 | LDU 256 36 | LDU 256 37 | } 38 | PUSHCONT { 39 | LDU 256 40 | LDU 256 41 | LDU 256 42 | LDREF 43 | ENDS 44 | CTOS 45 | } 46 | IFELSE 47 | LDU 256 48 | ENDS 49 | .loc arguments.sol, 0 50 | CALLREF { 51 | CALL $add_e022d77c_internal_macro$ 52 | } 53 | CALLREF { 54 | CALL $c7_to_c4$ 55 | } 56 | THROW 0 57 | 58 | .globl add_e022d77c_internal 59 | .type add_e022d77c_internal, @function 60 | CALL $add_e022d77c_internal_macro$ 61 | 62 | .macro add_e022d77c_internal_macro 63 | .loc arguments.sol, 8 64 | ACCEPT 65 | .loc arguments.sol, 9 66 | SWAP2 67 | ADD 68 | ROT 69 | ADD 70 | ADD 71 | GETGLOB 10 72 | ADD 73 | SETGLOB 10 74 | .loc arguments.sol, 0 75 | 76 | .macro get 77 | DROP 78 | GETGLOB 6 79 | THROWIFNOT 76 80 | GETGLOB 2 81 | ISNULL 82 | IFREF { 83 | CALL $c4_to_c7$ 84 | } 85 | .loc arguments.sol, 12 86 | OVER 87 | PUSHCONT { 88 | LDU 256 89 | LDREF 90 | ENDS 91 | CTOS 92 | LDU 256 93 | LDU 256 94 | } 95 | PUSHCONT { 96 | LDU 256 97 | LDU 256 98 | LDU 256 99 | LDREF 100 | ENDS 101 | CTOS 102 | } 103 | IFELSE 104 | LDU 256 105 | ENDS 106 | .loc arguments.sol, 0 107 | CALLREF { 108 | CALL $get_8a0722df_internal_macro$ 109 | } 110 | OVER 111 | PUSHCONT { 112 | PUSH S3 113 | CTOS 114 | LDU 2 115 | LDMSGADDR 116 | DROP 117 | NIP 118 | NEWC 119 | STSLICECONST xc 120 | STSLICE 121 | PUSHINT 4177966263 122 | STUR 130 123 | STU 256 124 | ENDC 125 | PUSHINT 0 126 | SENDRAWMSG 127 | } 128 | PUSHCONT { 129 | DROP 130 | } 131 | IFELSE 132 | IFREF { 133 | CALL $c7_to_c4$ 134 | } 135 | THROW 0 136 | 137 | .globl get_8a0722df_internal 138 | .type get_8a0722df_internal, @function 139 | CALL $get_8a0722df_internal_macro$ 140 | 141 | .macro get_8a0722df_internal_macro 142 | .loc arguments.sol, 13 143 | GETGLOB 10 144 | ROLL 4 145 | ADD 146 | ROLL 3 147 | ADD 148 | ROT 149 | ADD 150 | ADD 151 | .loc arguments.sol, 0 152 | 153 | .macro c4_to_c7 154 | PUSHROOT 155 | CTOS 156 | LDU 256 ; pubkey c4 157 | LDU 64 ; pubkey timestamp c4 158 | LDU 1 ; ctor flag 159 | NIP 160 | LDU 256 161 | ENDS 162 | SETGLOB 10 163 | SETGLOB 3 164 | SETGLOB 2 165 | 166 | .macro c4_to_c7_with_init_storage 167 | PUSHROOT 168 | CTOS 169 | SBITS 170 | GTINT 1 171 | PUSHCONT { 172 | PUSHINT 0 173 | PUSHROOT 174 | CTOS 175 | PLDDICT ; D 176 | PUSHINT 0 177 | SETGLOB 10 178 | PUSHINT 64 179 | DICTUGET 180 | THROWIFNOT 61 181 | PLDU 256 182 | SETGLOB 2 183 | PUSHINT 0 ; timestamp 184 | SETGLOB 3 185 | } 186 | IFREFELSE { 187 | CALL $c4_to_c7$ 188 | } 189 | 190 | .macro c7_to_c4 191 | GETGLOB 10 192 | GETGLOB 3 193 | GETGLOB 2 194 | NEWC 195 | STU 256 196 | STU 64 197 | STONE 198 | STU 256 199 | ENDC 200 | POPROOT 201 | 202 | .macro upd_only_time_in_c4 203 | PUSHROOT 204 | CTOS 205 | LDU 256 206 | LDU 64 207 | NIP 208 | GETGLOB 3 209 | ROT 210 | NEWC 211 | STU 256 212 | STU 64 213 | STSLICE 214 | ENDC 215 | POPROOT 216 | 217 | .internal-alias :main_internal, 0 218 | .internal :main_internal 219 | PUSHROOT 220 | CTOS 221 | SBITS 222 | NEQINT 1 223 | SETGLOB 6 224 | PUSH S2 225 | CTOS 226 | PLDU 4 227 | MODPOW2 1 228 | IFRET 229 | OVER 230 | SEMPTY ; isEmpty 231 | IFJMPREF { 232 | GETGLOB 6 233 | THROWIFNOT 76 234 | } 235 | OVER 236 | LDUQ 32 ; [funcId] body' ok 237 | THROWIFNOT 60 238 | OVER 239 | IFNOTJMPREF { 240 | GETGLOB 6 241 | THROWIFNOT 76 242 | } 243 | SWAP 244 | CALLREF { 245 | CALL $public_function_selector$ 246 | } 247 | THROW 60 248 | 249 | .internal-alias :main_external, -1 250 | .internal :main_external 251 | PUSHROOT 252 | CTOS 253 | SBITS 254 | NEQINT 1 255 | SETGLOB 6 256 | OVER 257 | CALLREF { 258 | CALL $c4_to_c7_with_init_storage$ 259 | } 260 | LDU 1 ; haveSign msgSlice 261 | SWAP 262 | PUSHCONT { 263 | PUSHPOW2 9 264 | LDSLICEX ; signatureSlice msgSlice 265 | DUP 266 | HASHSU ; signatureSlice msgSlice hashMsgSlice 267 | ROT 268 | GETGLOB 2 269 | CHKSIGNU ; msgSlice isSigned 270 | THROWIFNOT 40 271 | } 272 | IF 273 | LDU 64 ; timestamp msgSlice 274 | SWAP 275 | CALL $replay_protection_macro$ 276 | LDU 32 ; expireAt msgSlice 277 | SWAP 278 | NOW ; msgSlice expireAt now 279 | GREATER ; msgSlice expireAt>now 280 | THROWIFNOT 57 281 | LDU 32 ; funcId body 282 | SWAP 283 | CALLREF { 284 | CALL $public_function_selector$ 285 | } 286 | THROW 60 287 | 288 | .macro val 289 | DROP 290 | ENDS 291 | CALLREF { 292 | CALL $c4_to_c7$ 293 | } 294 | GETGLOB 10 295 | OVER 296 | PUSHCONT { 297 | PUSHSLICE xc0000000000000000000000000fd207a14 298 | NEWC 299 | STSLICE 300 | STU 256 301 | ENDC 302 | PUSHINT 0 303 | SENDRAWMSG 304 | } 305 | IF 306 | THROW 0 307 | 308 | .macro public_function_selector 309 | DUP 310 | PUSHINT 1756716863 311 | EQUAL 312 | IFJMPREF { 313 | CALL $constructor$ 314 | } 315 | DUP 316 | PUSHINT 2030482615 317 | EQUAL 318 | IFJMPREF { 319 | CALL $get$ 320 | } 321 | DUP 322 | PUSHINT 2057280665 323 | EQUAL 324 | IFJMPREF { 325 | CALL $add$ 326 | } 327 | DUP 328 | PUSHINT 2099280404 329 | EQUAL 330 | IFJMPREF { 331 | CALL $val$ 332 | } 333 | 334 | -------------------------------------------------------------------------------- /src/voting.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2021 EverX Labs Ltd. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | use crate::config::Config; 14 | use crate::helpers::{create_client_local, decode_msg_body}; 15 | use crate::multisig::{encode_transfer_body, MSIG_ABI, TRANSFER_WITH_COMMENT}; 16 | use crate::{call, message}; 17 | use serde_json::json; 18 | 19 | pub async fn create_proposal( 20 | config: &Config, 21 | addr: &str, 22 | keys: Option<&str>, 23 | dest: &str, 24 | text: &str, 25 | lifetime: u32, 26 | offline: bool, 27 | ) -> Result<(), String> { 28 | let payload = encode_transfer_body(text).await?; 29 | 30 | let params = json!({ 31 | "dest": dest, 32 | "value": 1000000, 33 | "bounce": false, 34 | "allBalance": false, 35 | "payload": payload, 36 | }) 37 | .to_string(); 38 | 39 | let keys = keys.map(|s| s.to_owned()); 40 | 41 | if offline { 42 | message::generate_message( 43 | config, 44 | addr, 45 | MSIG_ABI, 46 | "submitTransaction", 47 | ¶ms, 48 | keys, 49 | lifetime, 50 | false, 51 | None, 52 | None, 53 | None, 54 | ) 55 | .await 56 | } else { 57 | call::call_contract( 58 | config, 59 | addr, 60 | MSIG_ABI, 61 | "submitTransaction", 62 | ¶ms, 63 | keys, 64 | false, 65 | ) 66 | .await 67 | } 68 | } 69 | 70 | pub async fn vote( 71 | config: &Config, 72 | addr: &str, 73 | keys: Option<&str>, 74 | trid: &str, 75 | lifetime: u32, 76 | offline: bool, 77 | ) -> Result<(), String> { 78 | let params = json!({ 79 | "transactionId": trid, 80 | }) 81 | .to_string(); 82 | 83 | let keys = keys.map(|s| s.to_owned()); 84 | 85 | if offline { 86 | message::generate_message( 87 | config, 88 | addr, 89 | MSIG_ABI, 90 | "confirmTransaction", 91 | ¶ms, 92 | keys, 93 | lifetime, 94 | false, 95 | None, 96 | None, 97 | None, 98 | ) 99 | .await 100 | } else { 101 | call::call_contract( 102 | config, 103 | addr, 104 | MSIG_ABI, 105 | "confirmTransaction", 106 | ¶ms, 107 | keys, 108 | false, 109 | ) 110 | .await 111 | } 112 | } 113 | 114 | pub async fn decode_proposal(config: &Config, addr: &str, proposal_id: &str) -> Result<(), String> { 115 | // change to run 116 | let result = call::call_contract_with_result( 117 | config, 118 | addr, 119 | MSIG_ABI, 120 | "getTransactions", 121 | "{}", 122 | None, 123 | false, 124 | ) 125 | .await?; 126 | 127 | let txns = result["transactions"] 128 | .as_array() 129 | .ok_or(r#"failed to decode result: "transactions" array not found"#.to_string())?; 130 | 131 | for txn in txns { 132 | let txn_id = txn["id"] 133 | .as_str() 134 | .ok_or(r#"failed to parse transaction in list: "id" not found"#.to_string())?; 135 | 136 | if txn_id == proposal_id { 137 | let body = txn["payload"] 138 | .as_str() 139 | .ok_or(r#"failed to parse transaction in list: "payload" not found"#.to_string())?; 140 | let ton = create_client_local()?; 141 | let result = decode_msg_body(ton.clone(), TRANSFER_WITH_COMMENT, body, true, config) 142 | .await 143 | .map_err(|e| format!("failed to decode proposal payload: {}", e))?; 144 | 145 | let comment = String::from_utf8( 146 | hex::decode( 147 | result.value.ok_or("failed to get result value")?["comment"] 148 | .as_str() 149 | .ok_or("failed to obtain result comment")?, 150 | ) 151 | .map_err(|e| format!("failed to parse comment from transaction payload: {}", e))?, 152 | ) 153 | .map_err(|e| format!("failed to convert comment to string: {}", e))?; 154 | 155 | if !config.is_json { 156 | println!("Comment: {}", comment); 157 | } else { 158 | println!("{{"); 159 | println!(" \"Comment\": \"{}\"", comment); 160 | println!("}}"); 161 | } 162 | return Ok(()); 163 | } 164 | } 165 | if !config.is_json { 166 | println!("Proposal with id {} not found", proposal_id); 167 | } else { 168 | println!("{{"); 169 | println!( 170 | " \"Error\": \"Proposal with id {} not found\"", 171 | proposal_id 172 | ); 173 | println!("}}"); 174 | } 175 | Ok(()) 176 | } 177 | -------------------------------------------------------------------------------- /src/debot/term_signing_box.rs: -------------------------------------------------------------------------------- 1 | use super::term_browser::input; 2 | use crate::crypto::load_keypair; 3 | use crate::helpers::{read_keys, TonClient}; 4 | use ever_client::crypto::{ 5 | get_signing_box, remove_signing_box, KeyPair, RegisteredSigningBox, SigningBoxHandle, 6 | }; 7 | use ever_client::encoding::decode_abi_bigint; 8 | use std::io::{self, BufRead, BufReader, Read, Write}; 9 | 10 | pub(super) struct TerminalSigningBox { 11 | handle: SigningBoxHandle, 12 | client: TonClient, 13 | } 14 | 15 | impl TerminalSigningBox { 16 | pub async fn new( 17 | client: TonClient, 18 | possible_keys: Vec, 19 | reader: Option>, 20 | ) -> Result { 21 | let keys = { 22 | if let Some(mut reader) = reader { 23 | let mut writer = io::stdout(); 24 | input_keys(None, possible_keys, &mut reader, &mut writer, 3)? 25 | } else { 26 | let stdio = std::io::stdin(); 27 | let mut reader = stdio.lock(); 28 | let mut writer = io::stdout(); 29 | input_keys(None, possible_keys, &mut reader, &mut writer, 3)? 30 | } 31 | }; 32 | let handle = get_signing_box(client.clone(), keys) 33 | .await 34 | .map(|r| r.handle) 35 | .map_err(|e| e.to_string())?; 36 | 37 | Ok(Self { handle, client }) 38 | } 39 | 40 | pub async fn new_with_keypath(client: TonClient, keys_path: String) -> Result { 41 | let keys = read_keys(&keys_path).unwrap_or_default(); 42 | let handle = get_signing_box(client.clone(), keys) 43 | .await 44 | .map(|r| r.handle) 45 | .map_err(|e| e.to_string())?; 46 | 47 | Ok(Self { handle, client }) 48 | } 49 | 50 | pub fn handle(&self) -> SigningBoxHandle { 51 | self.handle.clone() 52 | } 53 | 54 | pub fn leak(&mut self) -> SigningBoxHandle { 55 | let handle = self.handle.clone(); 56 | self.handle = SigningBoxHandle(0); 57 | handle 58 | } 59 | } 60 | 61 | impl Drop for TerminalSigningBox { 62 | fn drop(&mut self) { 63 | if self.handle.0 != 0 { 64 | let _ = remove_signing_box( 65 | self.client.clone(), 66 | RegisteredSigningBox { 67 | handle: self.handle.clone(), 68 | }, 69 | ); 70 | } 71 | } 72 | } 73 | 74 | pub(super) fn input_keys( 75 | prompt: Option<&str>, 76 | possible_keys: Vec, 77 | reader: &mut R, 78 | writer: &mut W, 79 | tries: u8, 80 | ) -> Result 81 | where 82 | R: BufRead, 83 | W: Write, 84 | { 85 | let enter_str = prompt.unwrap_or_default(); 86 | let mut pair = Err("no keypair".to_string()); 87 | let mut format_pubkeys = String::new(); 88 | possible_keys 89 | .iter() 90 | .for_each(|x| format_pubkeys += &format!(" {},", x)); 91 | for _ in 0..tries { 92 | let value = input(enter_str, reader, writer); 93 | pair = load_keypair(&value).map_err(|e| { 94 | println!("Invalid keys: {}. Try again.", e); 95 | e 96 | }); 97 | if let Ok(ref keys) = pair { 98 | if !possible_keys.is_empty() { 99 | let pub_key_in_radix10 = decode_abi_bigint(&*("0x".to_string() + &*keys.public)) 100 | .unwrap() 101 | .to_string(); 102 | if !possible_keys.iter().any(|x| *x == pub_key_in_radix10) { 103 | println!("Unexpected keys."); 104 | println!( 105 | "Hint: enter keypair which contains one of the following public keys: {}", 106 | format_pubkeys 107 | ); 108 | } else { 109 | break; 110 | } 111 | } else { 112 | break; 113 | } 114 | } 115 | } 116 | pair 117 | } 118 | 119 | #[cfg(test)] 120 | mod tests { 121 | use super::*; 122 | use std::fs::File; 123 | 124 | const PUBLIC: &str = "9711a04f0b19474272bc7bae5472a8fbbb6ef71ce9c193f5ec3f5af808069a41"; 125 | const PRIVATE: &str = "cdf2a820517fa783b9b6094d15e650af92d485084ab217fc2c859f02d49623f3"; 126 | const SEED: &str = 127 | "episode polar pistol excite essence van cover fox visual gown yellow minute"; 128 | const KEYS_FILE: &str = "./keys.json"; 129 | 130 | fn create_keypair_file(name: &str) { 131 | let mut file = File::create(name).unwrap(); 132 | file.write_all( 133 | format!( 134 | r#"{{ 135 | "public": "{}", 136 | "secret": "{}" 137 | }}"#, 138 | PUBLIC, PRIVATE 139 | ) 140 | .as_bytes(), 141 | ) 142 | .unwrap(); 143 | } 144 | 145 | #[test] 146 | fn load_key_from_file() { 147 | let mut in_data = KEYS_FILE.as_bytes(); 148 | let mut out_data = vec![]; 149 | 150 | create_keypair_file(KEYS_FILE); 151 | let keys = input_keys(None, vec![], &mut in_data, &mut out_data, 1).unwrap(); 152 | assert_eq!(keys.public, PUBLIC); 153 | assert_eq!(keys.secret, PRIVATE); 154 | } 155 | 156 | #[test] 157 | fn load_key_from_seed() { 158 | let mut in_data = SEED.as_bytes(); 159 | let mut out_data = vec![]; 160 | 161 | let keys = input_keys(None, vec![], &mut in_data, &mut out_data, 1).unwrap(); 162 | assert_eq!(keys.public, PUBLIC); 163 | assert_eq!(keys.secret, PRIVATE); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/debot/interfaces/terminal.rs: -------------------------------------------------------------------------------- 1 | use super::dinterface::{ 2 | decode_answer_id, decode_bool_arg, decode_prompt, decode_string_arg, Printer, 3 | }; 4 | use crate::convert::convert_token; 5 | use crate::debot::term_browser::terminal_input; 6 | use ever_client::abi::Abi; 7 | use ever_client::debot::{DebotInterface, InterfaceResult}; 8 | use ever_client::encoding::decode_abi_bigint; 9 | use serde_json::{json, Value}; 10 | use std::io::Read; 11 | 12 | pub(super) const ID: &str = "8796536366ee21852db56dccb60bc564598b618c865fc50c8b1ab740bba128e3"; 13 | 14 | const ABI: &str = r#" 15 | { 16 | "ABI version": 2, 17 | "version": "2.2", 18 | "header": ["time"], 19 | "functions": [ 20 | { 21 | "name": "input", 22 | "id": "0x3955f72f", 23 | "inputs": [ 24 | {"name":"answerId","type":"uint32"}, 25 | {"name":"prompt","type":"string"}, 26 | {"name":"multiline","type":"bool"} 27 | ], 28 | "outputs": [ 29 | {"name":"value","type":"string"} 30 | ] 31 | }, 32 | { 33 | "name": "print", 34 | "id": "0x0ce649c2", 35 | "inputs": [ 36 | {"name":"answerId","type":"uint32"}, 37 | {"name":"message","type":"string"} 38 | ], 39 | "outputs": [ 40 | ] 41 | }, 42 | { 43 | "name": "printf", 44 | "id": "0x36a926ce", 45 | "inputs": [ 46 | {"name":"answerId","type":"uint32"}, 47 | {"name":"fmt","type":"string"}, 48 | {"name":"fargs","type":"cell"} 49 | ], 50 | "outputs": [ 51 | ] 52 | }, 53 | { 54 | "name": "constructor", 55 | "id": "0x68b55f3f", 56 | "inputs": [ 57 | ], 58 | "outputs": [ 59 | ] 60 | } 61 | ], 62 | "data": [ 63 | ], 64 | "events": [ 65 | ], 66 | "fields": [ 67 | {"name":"_pubkey","type":"uint256"}, 68 | {"name":"_timestamp","type":"uint64"}, 69 | {"name":"_constructorFlag","type":"bool"} 70 | ] 71 | } 72 | "#; 73 | 74 | pub struct Terminal { 75 | printer: Printer, 76 | } 77 | 78 | impl Terminal { 79 | pub fn new(printer: Printer) -> Self { 80 | Self { printer } 81 | } 82 | fn input_str(&self, args: &Value) -> InterfaceResult { 83 | let answer_id = decode_answer_id(args)?; 84 | let prompt = decode_prompt(args)?; 85 | let multiline = decode_bool_arg(args, "multiline")?; 86 | let mut value = String::new(); 87 | if multiline { 88 | println!("{}", &prompt); 89 | if cfg!(windows) { 90 | println!("(Ctrl+Z to exit)"); 91 | } else { 92 | println!("(Ctrl+D to exit)"); 93 | } 94 | std::io::stdin() 95 | .read_to_string(&mut value) 96 | .map_err(|e| format!("input error: {}", e))?; 97 | println!(); 98 | } else { 99 | value = terminal_input(&prompt, |_val| Ok(())); 100 | } 101 | Ok((answer_id, json!({ "value": value }))) 102 | } 103 | 104 | fn input_int(&self, args: &Value) -> InterfaceResult { 105 | let answer_id = decode_answer_id(args)?; 106 | let value = terminal_input(&decode_prompt(args)?, |val| { 107 | let _ = decode_abi_bigint(val).map_err(|e| format!("{}", e))?; 108 | Ok(()) 109 | }); 110 | Ok((answer_id, json!({ "value": value }))) 111 | } 112 | 113 | fn input_uint(&self, args: &Value) -> InterfaceResult { 114 | let answer_id = decode_answer_id(args)?; 115 | let value = terminal_input(&decode_prompt(args)?, |val| { 116 | let _ = decode_abi_bigint(val).map_err(|e| format!("{}", e))?; 117 | Ok(()) 118 | }); 119 | Ok((answer_id, json!({ "value": value }))) 120 | } 121 | 122 | fn input_tokens(&self, args: &Value) -> InterfaceResult { 123 | let answer_id = decode_answer_id(args)?; 124 | let mut nanotokens = String::new(); 125 | let _ = terminal_input(&decode_prompt(args)?, |val| { 126 | nanotokens = convert_token(val)?; 127 | Ok(()) 128 | }); 129 | Ok((answer_id, json!({ "value": nanotokens }))) 130 | } 131 | 132 | fn input_boolean(&self, args: &Value) -> InterfaceResult { 133 | let answer_id = decode_answer_id(args)?; 134 | println!("{}", decode_prompt(args)?); 135 | let mut yes_no = false; 136 | let _ = terminal_input("(y/n)", |val| { 137 | yes_no = match val.as_str() { 138 | "y" => true, 139 | "n" => false, 140 | _ => return Err("invalid enter".to_string()), 141 | }; 142 | Ok(()) 143 | }); 144 | Ok((answer_id, json!({ "value": yes_no }))) 145 | } 146 | 147 | pub async fn print(&self, args: &Value) -> InterfaceResult { 148 | let answer_id = decode_answer_id(args)?; 149 | let message = decode_string_arg(args, "message")?; 150 | self.printer.print(&message).await; 151 | Ok((answer_id, json!({}))) 152 | } 153 | } 154 | 155 | #[async_trait::async_trait] 156 | impl DebotInterface for Terminal { 157 | fn get_id(&self) -> String { 158 | ID.to_string() 159 | } 160 | 161 | fn get_abi(&self) -> Abi { 162 | Abi::Json(ABI.to_owned()) 163 | } 164 | 165 | async fn call(&self, func: &str, args: &Value) -> InterfaceResult { 166 | match func { 167 | "input" => self.input_str(args), 168 | "inputStr" => self.input_str(args), 169 | "inputInt" => self.input_int(args), 170 | "inputUint" => self.input_uint(args), 171 | "inputTons" => self.input_tokens(args), 172 | "inputBoolean" => self.input_boolean(args), 173 | "print" => self.print(args).await, 174 | _ => Err(format!("function \"{}\" is not implemented", func)), 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/completion.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2021 EverX Labs Ltd. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | extern crate core; 15 | 16 | use std::collections::BTreeMap; 17 | use std::path::PathBuf; 18 | use serde::{Deserialize, Serialize}; 19 | use ever_client::abi::AbiContract; 20 | 21 | #[derive(Serialize, Deserialize, Clone)] 22 | pub struct ContractData { 23 | pub abi_path: Option, 24 | pub address: Option, 25 | pub key_path: Option, 26 | } 27 | 28 | #[derive(Serialize, Deserialize, Clone)] 29 | pub struct FullConfig { 30 | pub config: Config, 31 | endpoints_map: BTreeMap>, 32 | pub aliases: BTreeMap, 33 | pub path: String, 34 | } 35 | 36 | #[derive(Serialize, Deserialize, Clone)] 37 | pub struct Config { 38 | pub url: String, 39 | pub wc: i32, 40 | pub addr: Option, 41 | pub method: Option, 42 | pub parameters: Option, 43 | pub wallet: Option, 44 | pub pubkey: Option, 45 | pub abi_path: Option, 46 | pub keys_path: Option, 47 | pub retries: u8, 48 | pub timeout: u32, 49 | pub message_processing_timeout: u32, 50 | pub out_of_sync_threshold: u32, 51 | pub is_json: bool, 52 | pub depool_fee: f32, 53 | pub lifetime: u32, 54 | pub no_answer: bool, 55 | pub balance_in_tons: bool, 56 | pub local_run: bool, 57 | pub async_call: bool, 58 | pub debug_fail: String, 59 | pub endpoints: Vec, 60 | } 61 | 62 | const CONFIG_BASE_NAME: &str = "ever-cli.conf.json"; 63 | 64 | fn print_paths(prefix: &str) { 65 | let folder = if !prefix.contains('/') { 66 | "./" 67 | } else { 68 | prefix.trim_end_matches(|c| c != '/') 69 | }; 70 | let paths = std::fs::read_dir(folder); 71 | if paths.is_err() { 72 | return; 73 | } 74 | let mut saved_path: Vec = vec![]; 75 | for path in paths.unwrap().flatten() { 76 | let path = path.path(); 77 | let path_str = path.to_str().unwrap(); 78 | if path_str.starts_with(prefix) { 79 | saved_path.push(path); 80 | } 81 | } 82 | if saved_path.len() == 1 && saved_path[0].is_dir() { 83 | let paths = std::fs::read_dir(saved_path[0].to_str().unwrap()); 84 | for path in paths.unwrap() { 85 | println!("{}", path.unwrap().path().to_str().unwrap()); 86 | } 87 | } else { 88 | for path in saved_path { 89 | println!("{}{}", path.to_str().unwrap(), if path.is_dir() {"/"} else {""}); 90 | } 91 | } 92 | } 93 | 94 | fn main() { 95 | let args : Vec = std::env::args().collect(); 96 | let word_being_completed = &args[2]; 97 | let prev_word = args.last().unwrap(); 98 | 99 | let cmd_line = std::env::var("COMP_LINE"); 100 | if cmd_line.is_err() { 101 | print_paths(word_being_completed); 102 | return; 103 | } 104 | let words = cmd_line.unwrap().split(' ').map(|s| s.to_string()) 105 | .collect::>(); 106 | let mut options_map = BTreeMap::new(); 107 | for (index, word) in words.iter().enumerate() { 108 | if word.starts_with('-') && index != words.len() - 1 { 109 | options_map.insert(word.as_str(), words[index + 1].as_str()); 110 | } 111 | } 112 | let config_path = options_map.get(&"-c") 113 | .or(options_map.get(&"--config") 114 | .or(Some(&CONFIG_BASE_NAME))).unwrap(); 115 | let conf_str = std::fs::read_to_string(config_path).ok().unwrap_or_default(); 116 | let config: serde_json::Result = serde_json::from_str(&conf_str); 117 | if config.is_err() { 118 | print_paths(word_being_completed); 119 | return; 120 | } 121 | let config = config.unwrap(); 122 | let aliases = config.aliases; 123 | if prev_word == "--addr" { 124 | if word_being_completed.is_empty() { 125 | for alias in aliases.keys() { 126 | println!("{}", alias); 127 | } 128 | } else { 129 | for alias in aliases { 130 | if alias.0.starts_with(word_being_completed) { 131 | println!("{}", alias.0); 132 | } 133 | } 134 | } 135 | return; 136 | } 137 | if prev_word == "-m" || prev_word == "--method" { 138 | let abi_path = match options_map.get(&"--abi") { 139 | Some(path) => Some(path.to_string()), 140 | None => { 141 | if (options_map.contains_key("--addr")) && aliases.contains_key(&options_map.get("--addr").unwrap().to_string()) { 142 | aliases.get(&options_map.get("--addr").unwrap().to_string()).unwrap().clone().abi_path 143 | } else { 144 | None 145 | } 146 | } 147 | }.or(config.config.abi_path); 148 | if abi_path.is_none() { 149 | return; 150 | } 151 | if let Ok(abi) = std::fs::read_to_string(abi_path.unwrap()) { 152 | if let Ok(abi_contract) = serde_json::from_str::(&abi) { 153 | for function in abi_contract.functions { 154 | if function.name.starts_with(word_being_completed) { 155 | println!("{}", function.name); 156 | } 157 | } 158 | } 159 | } 160 | return; 161 | } 162 | print_paths(word_being_completed); 163 | } -------------------------------------------------------------------------------- /src/debot/interfaces/dinterface.rs: -------------------------------------------------------------------------------- 1 | use super::echo::Echo; 2 | use super::stdout::Stdout; 3 | use super::{ 4 | AddressInput, AmountInput, ConfirmInput, EncryptionBoxInput, InputInterface, Menu, NumberInput, 5 | SigningBoxInput, Terminal, UserInfo, 6 | }; 7 | use crate::config::Config; 8 | use crate::debot::ChainProcessor; 9 | use crate::helpers::TonClient; 10 | use num_bigint::BigInt; 11 | use num_traits::cast::NumCast; 12 | use serde_json::Value; 13 | use std::collections::HashMap; 14 | use std::sync::Arc; 15 | use tokio::sync::RwLock; 16 | 17 | use ever_client::debot::{DebotInterface, DebotInterfaceExecutor}; 18 | use ever_client::encoding::{decode_abi_bigint, decode_abi_number}; 19 | 20 | pub struct SupportedInterfaces { 21 | client: TonClient, 22 | interfaces: HashMap>, 23 | } 24 | 25 | #[async_trait::async_trait] 26 | impl DebotInterfaceExecutor for SupportedInterfaces { 27 | fn get_interfaces(&self) -> &HashMap> { 28 | &self.interfaces 29 | } 30 | fn get_client(&self) -> TonClient { 31 | self.client.clone() 32 | } 33 | } 34 | 35 | /// Helper struct used only inside SupportedInterfaces. 36 | struct InterfaceWrapper { 37 | processor: Arc>, 38 | } 39 | impl InterfaceWrapper { 40 | fn wrap( 41 | &self, 42 | iface: Arc, 43 | ) -> Arc { 44 | Arc::new(InputInterface::new(iface, self.processor.clone())) 45 | } 46 | } 47 | 48 | impl SupportedInterfaces { 49 | pub fn new(client: TonClient, config: &Config, processor: Arc>) -> Self { 50 | let mut interfaces = HashMap::new(); 51 | 52 | let iw = InterfaceWrapper { 53 | processor: processor.clone(), 54 | }; 55 | 56 | let iface: Arc = 57 | iw.wrap(Arc::new(AddressInput::new(config.clone()))); 58 | interfaces.insert(iface.get_id(), iface); 59 | 60 | let iface: Arc = iw.wrap(Arc::new(AmountInput::new())); 61 | interfaces.insert(iface.get_id(), iface); 62 | 63 | let iface: Arc = iw.wrap(Arc::new(NumberInput::new())); 64 | interfaces.insert(iface.get_id(), iface); 65 | 66 | let iface: Arc = iw.wrap(Arc::new(ConfirmInput::new())); 67 | interfaces.insert(iface.get_id(), iface); 68 | 69 | let iface: Arc = Arc::new(Stdout::new()); 70 | interfaces.insert(iface.get_id(), iface); 71 | 72 | let iface: Arc = Arc::new(Echo::new()); 73 | interfaces.insert(iface.get_id(), iface); 74 | 75 | let iface: Arc = 76 | iw.wrap(Arc::new(Terminal::new(Printer { processor }))); 77 | interfaces.insert(iface.get_id(), iface); 78 | 79 | let iface: Arc = iw.wrap(Arc::new(Menu::new())); 80 | interfaces.insert(iface.get_id(), iface); 81 | 82 | let iface: Arc = 83 | Arc::new(SigningBoxInput::new(client.clone(), iw.processor.clone())); 84 | interfaces.insert(iface.get_id(), iface); 85 | 86 | let iface: Arc = 87 | iw.wrap(Arc::new(UserInfo::new(client.clone(), config.clone()))); 88 | interfaces.insert(iface.get_id(), iface); 89 | 90 | let iface: Arc = 91 | Arc::new(EncryptionBoxInput::new(client.clone())); 92 | interfaces.insert(iface.get_id(), iface); 93 | 94 | Self { client, interfaces } 95 | } 96 | } 97 | 98 | pub struct Printer { 99 | processor: Arc>, 100 | } 101 | 102 | impl Printer { 103 | pub async fn print(&self, msg: &str) { 104 | self.processor.read().await.print(msg); 105 | } 106 | } 107 | 108 | pub fn decode_answer_id(args: &Value) -> Result { 109 | u32::from_str_radix( 110 | args["answerId"] 111 | .as_str() 112 | .ok_or("answer id not found in argument list".to_string())?, 113 | 10, 114 | ) 115 | .map_err(|e| format!("{}", e)) 116 | } 117 | 118 | pub fn decode_arg(args: &Value, name: &str) -> Result { 119 | args[name] 120 | .as_str() 121 | .ok_or(format!("\"{}\" not found", name)) 122 | .map(|x| x.to_string()) 123 | } 124 | 125 | pub fn decode_bool_arg(args: &Value, name: &str) -> Result { 126 | args[name] 127 | .as_bool() 128 | .ok_or(format!("\"{}\" not found", name)) 129 | } 130 | 131 | pub fn decode_string_arg(args: &Value, name: &str) -> Result { 132 | decode_arg(args, name) 133 | } 134 | 135 | pub fn decode_nonce(args: &Value) -> Result { 136 | decode_arg(args, "nonce") 137 | } 138 | 139 | pub fn decode_prompt(args: &Value) -> Result { 140 | decode_string_arg(args, "prompt") 141 | } 142 | 143 | pub fn decode_num_arg(args: &Value, name: &str) -> Result 144 | where 145 | T: NumCast, 146 | { 147 | let num_str = decode_arg(args, name)?; 148 | decode_abi_number::(&num_str) 149 | .map_err(|e| format!("failed to parse integer \"{}\": {}", num_str, e)) 150 | } 151 | 152 | pub fn decode_int256(args: &Value, name: &str) -> Result { 153 | let num_str = decode_arg(args, name)?; 154 | decode_abi_bigint(&num_str) 155 | .map_err(|e| format!("failed to decode integer \"{}\": {}", num_str, e)) 156 | } 157 | 158 | pub fn decode_array(args: &Value, name: &str, validator: F) -> Result, String> 159 | where 160 | F: Fn(&Value) -> Option, 161 | { 162 | let array = args[name] 163 | .as_array() 164 | .ok_or(format!("\"{}\" is invalid: must be array", name))?; 165 | let mut strings = vec![]; 166 | for elem in array { 167 | strings.push(validator(elem).ok_or("invalid array element type".to_string())?); 168 | } 169 | Ok(strings) 170 | } 171 | -------------------------------------------------------------------------------- /src/debot/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 EverX. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | mod callbacks; 14 | mod interfaces; 15 | mod pipechain; 16 | mod processor; 17 | pub mod term_browser; 18 | mod term_encryption_box; 19 | mod term_signing_box; 20 | 21 | use crate::config::Config; 22 | use crate::helpers::load_ton_address; 23 | use callbacks::Callbacks; 24 | use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; 25 | pub use interfaces::dinterface::SupportedInterfaces; 26 | use pipechain::{ApproveKind, ChainLink, PipeChain}; 27 | use processor::{ChainProcessor, ProcessorError}; 28 | use simplelog::*; 29 | use term_browser::{action_input, input, run_debot_browser, terminal_input}; 30 | 31 | pub fn create_debot_command<'a, 'b>() -> App<'a, 'b> { 32 | SubCommand::with_name("debot") 33 | .about("Debot commands.") 34 | .setting(AppSettings::AllowLeadingHyphen) 35 | .setting(AppSettings::TrailingVarArg) 36 | .setting(AppSettings::DontCollapseArgsInUsage) 37 | .arg(Arg::with_name("DEBUG").long("--debug").short("-d")) 38 | .subcommand( 39 | SubCommand::with_name("fetch") 40 | .setting(AppSettings::AllowLeadingHyphen) 41 | .arg( 42 | Arg::with_name("ADDRESS") 43 | .required(true) 44 | .help("DeBot TON address."), 45 | ), 46 | ) 47 | .subcommand( 48 | SubCommand::with_name("start") 49 | .setting(AppSettings::AllowLeadingHyphen) 50 | .arg( 51 | Arg::with_name("ADDRESS") 52 | .required(true) 53 | .help("DeBot TON address."), 54 | ) 55 | .arg( 56 | Arg::with_name("PIPECHAIN") 57 | .short("m") 58 | .long("pipechain") 59 | .takes_value(true) 60 | .help("Path to the DeBot Manifest."), 61 | ) 62 | .arg( 63 | Arg::with_name("SIGNKEY") 64 | .short("s") 65 | .long("signkey") 66 | .takes_value(true) 67 | .help("Define keypair to auto sign transactions."), 68 | ), 69 | ) 70 | .subcommand( 71 | SubCommand::with_name("invoke") 72 | .setting(AppSettings::AllowLeadingHyphen) 73 | .arg( 74 | Arg::with_name("ADDRESS") 75 | .required(true) 76 | .help("Debot TON address."), 77 | ) 78 | .arg( 79 | Arg::with_name("MESSAGE") 80 | .required(true) 81 | .help("Message to DeBot encoded as base64/base64url."), 82 | ), 83 | ) 84 | } 85 | 86 | pub async fn debot_command(m: &ArgMatches<'_>, config: Config) -> Result<(), String> { 87 | let debug = m.is_present("DEBUG"); 88 | let log_conf = ConfigBuilder::new() 89 | .add_filter_ignore_str("executor") 90 | .add_filter_ignore_str("hyper") 91 | .add_filter_ignore_str("reqwest") 92 | .build(); 93 | 94 | let mut loggers: Vec> = vec![]; 95 | let file = std::fs::File::create("debot_err.log"); 96 | if file.is_ok() { 97 | loggers.push(WriteLogger::new( 98 | LevelFilter::Error, 99 | log_conf.clone(), 100 | file.unwrap(), 101 | )); 102 | } 103 | 104 | if debug { 105 | loggers.push(TermLogger::new( 106 | LevelFilter::Debug, 107 | log_conf.clone(), 108 | TerminalMode::Mixed, 109 | )); 110 | } 111 | CombinedLogger::init(loggers).unwrap(); 112 | 113 | if let Some(m) = m.subcommand_matches("fetch") { 114 | return fetch_command(m, config).await; 115 | } 116 | if let Some(m) = m.subcommand_matches("start") { 117 | return fetch_command(m, config).await; 118 | } 119 | if let Some(m) = m.subcommand_matches("invoke") { 120 | return invoke_command(m, config).await; 121 | } 122 | Err("unknown debot command".to_owned()) 123 | } 124 | 125 | async fn fetch_command(m: &ArgMatches<'_>, config: Config) -> Result<(), String> { 126 | let addr = m.value_of("ADDRESS"); 127 | let pipechain = m.value_of("PIPECHAIN"); 128 | let signkey_path = m 129 | .value_of("SIGNKEY") 130 | .map(|x| x.to_owned()) 131 | .or(config.keys_path.clone()); 132 | let is_json = config.is_json; 133 | let pipechain = if let Some(filename) = pipechain { 134 | let manifest_raw = std::fs::read_to_string(filename) 135 | .map_err(|e| format!("failed to read pipechain: {}", e))?; 136 | serde_json::from_str(&manifest_raw) 137 | .map_err(|e| format!("failed to parse pipechain: {}", e))? 138 | } else { 139 | PipeChain::new() 140 | }; 141 | let addr = load_ton_address(addr.unwrap(), &config)?; 142 | let result = run_debot_browser(addr.as_str(), config, pipechain, signkey_path).await; 143 | match result { 144 | Ok(Some(arg)) => { 145 | if !is_json { 146 | println!("Returned value:"); 147 | } 148 | println!("{:#}", arg); 149 | Ok(()) 150 | } 151 | Err(err) if err.contains("NoMoreChainlinks") => Ok(()), 152 | result => result.map(|_| ()), 153 | } 154 | } 155 | 156 | async fn invoke_command(m: &ArgMatches<'_>, config: Config) -> Result<(), String> { 157 | let addr = m.value_of("ADDRESS"); 158 | load_ton_address(addr.unwrap(), &config)?; 159 | let _ = m.value_of("MESSAGE").unwrap().to_owned(); 160 | Ok(()) 161 | } 162 | -------------------------------------------------------------------------------- /src/deploy.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 EverX. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | use crate::call::{emulate_locally, process_message, send_message_and_wait}; 14 | use crate::config::FullConfig; 15 | use crate::crypto::load_keypair; 16 | use crate::helpers::{create_client_verbose, create_client_with_signature_id, load_abi, now_ms}; 17 | use crate::message::{display_generated_message, EncodedMessage}; 18 | use crate::{Config, SignatureIDType}; 19 | use ever_client::abi::{ 20 | encode_message, Abi, CallSet, DeploySet, FunctionHeader, ParamsOfEncodeMessage, Signer, 21 | }; 22 | use ever_client::crypto::KeyPair; 23 | 24 | pub async fn deploy_contract( 25 | full_config: &mut FullConfig, 26 | tvc: &str, 27 | abi: &str, 28 | params: &str, 29 | keys_file: Option, 30 | wc: i32, 31 | is_fee: bool, 32 | alias: Option<&str>, 33 | method: String, 34 | ) -> Result<(), String> { 35 | let config = &full_config.config; 36 | let ton = create_client_verbose(config)?; 37 | 38 | if !is_fee && !config.is_json { 39 | println!("Deploying..."); 40 | } 41 | 42 | let (msg, addr) = prepare_deploy_message( 43 | tvc, 44 | abi, 45 | params, 46 | keys_file.clone(), 47 | wc, 48 | &full_config.config, 49 | None, 50 | method, 51 | ) 52 | .await?; 53 | 54 | let enc_msg = encode_message(ton.clone(), msg.clone()) 55 | .await 56 | .map_err(|e| format!("failed to create inbound message: {}", e))?; 57 | 58 | if config.local_run || is_fee { 59 | emulate_locally(ton.clone(), addr.as_str(), enc_msg.message.clone(), is_fee).await?; 60 | if is_fee { 61 | return Ok(()); 62 | } 63 | } 64 | 65 | if config.async_call { 66 | let abi = load_abi(abi, config).await?; 67 | send_message_and_wait(ton, Some(abi), enc_msg.message, config).await?; 68 | } else { 69 | process_message(ton.clone(), msg, config) 70 | .await 71 | .map_err(|e| format!("{:#}", e))?; 72 | } 73 | 74 | if !config.is_json { 75 | if !config.async_call { 76 | println!("Transaction succeeded."); 77 | } 78 | println!("Contract deployed at address: {}", addr); 79 | } else { 80 | println!("{{}}"); 81 | } 82 | if let Some(alias) = alias { 83 | full_config.add_alias(alias, Some(addr), Some(abi.to_string()), keys_file)?; 84 | } 85 | Ok(()) 86 | } 87 | 88 | pub async fn generate_deploy_message( 89 | tvc: &str, 90 | abi: &str, 91 | params: &str, 92 | keys_file: Option, 93 | wc: i32, 94 | is_raw: bool, 95 | output: Option<&str>, 96 | config: &Config, 97 | signature_id: Option, 98 | method: String, 99 | ) -> Result<(), String> { 100 | let (client, signature_id) = create_client_with_signature_id(config, signature_id)?; 101 | 102 | let (msg, addr) = prepare_deploy_message( 103 | tvc, 104 | abi, 105 | params, 106 | keys_file, 107 | wc, 108 | config, 109 | signature_id, 110 | method.clone(), 111 | ) 112 | .await?; 113 | let msg = encode_message(client, msg) 114 | .await 115 | .map_err(|e| format!("failed to create inbound message: {}", e))?; 116 | 117 | let msg = EncodedMessage { 118 | message: msg.message, 119 | message_id: msg.message_id, 120 | expire: None, 121 | address: addr.to_owned(), 122 | }; 123 | display_generated_message(&msg, method.as_str(), is_raw, output, config.is_json)?; 124 | if !config.is_json { 125 | println!("Contract's address: {}", addr); 126 | println!("Succeeded."); 127 | } 128 | Ok(()) 129 | } 130 | 131 | pub async fn prepare_deploy_message( 132 | tvc: &str, 133 | abi: &str, 134 | params: &str, 135 | keys_file: Option, 136 | wc: i32, 137 | config: &Config, 138 | signature_id: Option, 139 | method: String, 140 | ) -> Result<(ParamsOfEncodeMessage, String), String> { 141 | let abi = load_abi(abi, config).await?; 142 | 143 | let keys = keys_file.map(|k| load_keypair(&k)).transpose()?; 144 | 145 | let tvc_bytes = 146 | std::fs::read(tvc).map_err(|e| format!("failed to read smart contract file {tvc}: {e}"))?; 147 | 148 | prepare_deploy_message_params( 149 | &tvc_bytes, 150 | abi, 151 | method, 152 | now_ms(), 153 | params, 154 | keys, 155 | wc, 156 | signature_id, 157 | ) 158 | .await 159 | } 160 | 161 | pub async fn prepare_deploy_message_params( 162 | tvc_bytes: &[u8], 163 | abi: Abi, 164 | function_name: String, 165 | time: u64, 166 | params: &str, 167 | keys: Option, 168 | wc: i32, 169 | signature_id: Option, 170 | ) -> Result<(ParamsOfEncodeMessage, String), String> { 171 | let tvc = base64::encode(tvc_bytes); 172 | 173 | let data_map_supported = abi.abi().unwrap().data_map_supported(); 174 | let address = if data_map_supported { 175 | crate::helpers::calc_acc_address( 176 | tvc_bytes, 177 | wc, 178 | keys.as_ref().map(|k| k.public.clone()), 179 | None, 180 | abi.clone(), 181 | ) 182 | .await? 183 | } else { 184 | let tvc_cell = ever_block::boc::read_single_root_boc(tvc_bytes).unwrap(); 185 | let tvc_hash = tvc_cell.repr_hash(); 186 | format!("{}:{}", wc, tvc_hash.as_hex_string()) 187 | }; 188 | 189 | let header = Some(FunctionHeader { 190 | time: Some(time), 191 | ..Default::default() 192 | }); 193 | let deploy_set = Some(DeploySet { 194 | tvc: Some(tvc), 195 | workchain_id: Some(wc), 196 | ..Default::default() 197 | }); 198 | let params = serde_json::from_str(params) 199 | .map_err(|e| format!("function arguments is not a json: {}", e))?; 200 | let call_set = Some(CallSet { 201 | function_name, 202 | input: Some(params), 203 | header, 204 | }); 205 | let signer = if let Some(keys) = keys { 206 | Signer::Keys { keys } 207 | } else { 208 | Signer::None 209 | }; 210 | Ok(( 211 | ParamsOfEncodeMessage { 212 | abi, 213 | address: Some(address.clone()), 214 | deploy_set, 215 | call_set, 216 | signer, 217 | signature_id, 218 | ..Default::default() 219 | }, 220 | address, 221 | )) 222 | } 223 | --------------------------------------------------------------------------------