├── .github ├── CODEOWNERS └── workflows │ └── cicd.yaml ├── .gitignore ├── .prettierignore ├── .travis.yml.disabled ├── LICENSE ├── README.md ├── ci_scilla_type_check.sh ├── example ├── zrc1 │ ├── approve.js │ ├── balance_of.js │ ├── burn.js │ ├── configure_minter.js │ ├── deploy.js │ ├── get_approved.js │ ├── get_token_uri.js │ ├── is_approved_for_all.js │ ├── is_owner.js │ ├── mint.js │ ├── name.js │ ├── nonfungible-token.scilla │ ├── set_approve_for_all.js │ ├── symbol.js │ ├── total_supply.js │ ├── transfer.js │ └── transfer_from.js ├── zrc2 │ ├── auth_operator.js │ ├── burn.js │ ├── decrease_allowance.js │ ├── deploy.js │ ├── increase_allowance.js │ ├── is_operator_for.js │ ├── mint.js │ ├── operator_send.js │ ├── revoke_operator.js │ ├── transfer.js │ └── transfer_from.js ├── zrc3 │ ├── deploy.js │ ├── metatransfer.js │ └── transfer.js └── zrc4 │ ├── README.md │ ├── add_funds.js │ ├── deploy.js │ ├── execute_transaction.js │ ├── get_balance.js │ ├── get_owners.js │ ├── get_transactions.js │ ├── revoke_signature.js │ ├── sign_transaction.js │ └── submit_transaction.js ├── package-lock.json ├── package.json ├── reference-contracts ├── FungibleToken-Burnable.scilla ├── FungibleToken-Mintable.scilla ├── FungibleToken-Operator.scilla ├── FungibleToken.scilla ├── MetaFungibleToken.scilla ├── multisig_wallet.scilla ├── nonfungible-token.scilla ├── style-guide.md └── zrc6.scilla ├── tests ├── globalConfig.ts ├── testutils.ts ├── zrc2 │ └── fungible-token-burnable │ │ ├── config.ts │ │ └── fungibleBurnableToken.test.ts └── zrc6 │ ├── config.ts │ ├── zrc6.approval.test.ts │ ├── zrc6.mint.test.ts │ └── zrc6.token.test.ts ├── tsconfig.json └── zrcs ├── zrc-0.md ├── zrc-1.md ├── zrc-2.md ├── zrc-3.md ├── zrc-4.md ├── zrc-5.md ├── zrc-6.md └── zrc-7.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | ########## Global owners ########## 2 | 3 | * @jjcnn @anton-trunov @renlulu @teye 4 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | build: 8 | name: CI 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - name: Checkout scm 13 | uses: actions/checkout@v3 14 | 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16.x 18 | cache: npm 19 | 20 | - name: Run test 21 | run: | 22 | ./ci_scilla_type_check.sh && npm i && npm test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | example 2 | -------------------------------------------------------------------------------- /.travis.yml.disabled: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 16 4 | 5 | sudo: required 6 | services: 7 | - docker 8 | 9 | cache: 10 | directories: 11 | - docker_images 12 | 13 | before_cache: 14 | - docker save -o docker_images/images.tar $(docker images -aq) 15 | 16 | before_script: 17 | - docker load -i docker_images/images.tar || true 18 | 19 | script: ./ci_scilla_type_check.sh && npm i && npm test 20 | 21 | skip_cleanup: true 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 - present Zilliqa Research Pte. Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | ZRC (Zilliqa Reference Contracts) 4 |

5 | 6 | Contract standards for the Zilliqa platform 7 | 8 |
9 |
10 | 11 | [![Build Status](https://app.travis-ci.com/Zilliqa/ZRC.svg?branch=master)](https://app.travis-ci.com/Zilliqa/ZRC) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) 12 | 13 | The Zilliqa Reference Contracts (ZRCs) are the contract standards for the Zilliqa platform. 14 | 15 | | ZRC | Title | 16 | | :---------------------: | ----------------------------------------------- | 17 | | [ZRC-7](/zrcs/zrc-7.md) | NFT Metadata Standard | 18 | | [ZRC-6](/zrcs/zrc-6.md) | Non-Fungible Token Standard | 19 | | [ZRC-5](/zrcs/zrc-5.md) | Convention for Deposit of ZIL | 20 | | [ZRC-4](/zrcs/zrc-4.md) | Standard for Multisig Wallet | 21 | | [ZRC-3](/zrcs/zrc-3.md) | Standard for Metatransactions | 22 | | [ZRC-2](/zrcs/zrc-2.md) | Standard for Fungible Tokens | 23 | | [ZRC-1](/zrcs/zrc-1.md) | Standard for Non Fungible Tokens _(deprecated)_ | 24 | 25 | ## Contributing 26 | 27 | 1. Review [ZRC-0](https://github.com/Zilliqa/ZRC/blob/master/zrcs/zrc-0.md). 28 | 2. Fork the repository by clicking "Fork" in the top right. 29 | 3. Add your ZRC to your fork of the repository. There is a template ZRC [here](https://github.com/Zilliqa/ZRC/blob/master/zrcs/zrc-1.md). 30 | 4. Submit a Pull Request to [Zilliqa's ZRC repository](https://github.com/Zilliqa/ZRC). 31 | 32 | Your first PR should be a first draft of the final ZRC. An editor will manually review the first PR for a new ZRC and assign it a number before merging it. Make sure you include a `discussions-to header` with the URL to a discussion forum or open GitHub issue where people can discuss the ZRC as a whole. 33 | 34 | If your ZRC requires images, the image files should be included in a subdirectory of the assets folder for that ZRC as follow: `assets/zrc-X` (for zrc X). When linking to an image in the ZRC, use relative links such as `../assets/zrc-X/image.png`. 35 | 36 | When you believe your ZRC is ready to progress past the 'Draft' phase, you should go to our [Zilliqa Official Discord](https://discord.gg/XMRE9tt) server and ask to have your issue added to the next community dev call where it can be discussed for inclusion in a future platform upgrade. If the community agrees to include it, the ZRC editors will update the state of your ZRC to 'Approved'. 37 | 38 | ## ZRC Status 39 | 40 | 1. **Draft** - a preliminary version of the ZRC that is not yet ready for submission. 41 | 2. **Ready** - a preliminary version of the ZRC that is ready for review by a wide audience. 42 | 3. **Approved** - a finalized version of the ZRC that has been in the 'Ready' state for at least 2 weeks and any technical changes that were requested have been addressed by the author. 43 | 4. **Implemented** - a finalized version of the ZRC that the Core Devs have decided to implement and release. 44 | 45 | ## Contract Testing 46 | 47 | ### `npm test` 48 | 49 | Runs contract tests using [Isolated Server container](https://hub.docker.com/r/zilliqa/zilliqa-isolated-server), [Jest](https://jestjs.io/), and [Scilla JSON Utils](https://github.com/Zilliqa/scilla-json-utils) 50 | 51 | ## License 52 | 53 | This project is open source software licensed as [MIT](https://github.com/zilliqa/zrc/blob/master/LICENSE). 54 | -------------------------------------------------------------------------------- /ci_scilla_type_check.sh: -------------------------------------------------------------------------------- 1 | container=`docker run -d -p 5555:5555 --entrypoint isolatedServer zilliqa/zilliqa-isolated-server:a01fe00 -t 1000 -u 0 -f boot.json` 2 | echo $container 3 | 4 | scilla="/scilla/0/" 5 | checker="${scilla}bin/scilla-checker" 6 | stdlib="${scilla}src/stdlib" 7 | gas_limit="9999999" 8 | 9 | function quit { 10 | rm_container=`docker stop $container | xargs docker rm` 11 | echo $rm_container 12 | exit $1 13 | } 14 | 15 | for src in reference-contracts/*.scilla; do 16 | file="zrc_${src##*/}" 17 | check="docker exec $container $checker -libdir $stdlib -gaslimit $gas_limit ${scilla}${file}" 18 | copy="docker cp ./$src $container:${scilla}/$file" 19 | cleanup="docker exec $container rm -rf ${scilla}/$file" 20 | 21 | $copy > /dev/null || { echo "copy failed: $file"; quit 1; } 22 | $check > /dev/null || { echo "type-checking failed: $file"; quit 1; } 23 | $cleanup > /dev/null || { echo "cleanup failed: $file"; quit 1; } 24 | 25 | echo "PASS:$file" 26 | done 27 | 28 | quit 0 29 | -------------------------------------------------------------------------------- /example/zrc1/approve.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'approve', 29 | [ 30 | { 31 | vname: 'to', 32 | type: 'ByStr20', 33 | value: '0xad7a7a32f0718c4e9718faf4b5f8c7c86ffa7fe2', 34 | }, 35 | { 36 | vname: 'token_id', 37 | type: 'Uint256', 38 | value: '2', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | 50 | // check the pending status 51 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 52 | console.log(`Pending status is: `); 53 | console.log(pendingStatus.result); 54 | 55 | // process confirm 56 | console.log(`The transaction id is:`, callTx.id); 57 | console.log(`Waiting transaction be confirmed`); 58 | const confirmedTxn = await callTx.confirm(callTx.id); 59 | 60 | console.log(`The transaction status is:`); 61 | console.log(JSON.stringify(confirmedTxn.receipt)); 62 | 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /example/zrc1/balance_of.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'balanceOf', 29 | [ 30 | { 31 | vname: 'address', 32 | type: 'ByStr20', 33 | value: '0x501a70ffeaa3f31c1cacce3479e74713546baa44', 34 | } 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | 45 | // check the pending status 46 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 47 | console.log(`Pending status is: `); 48 | console.log(pendingStatus.result); 49 | 50 | // process confirm 51 | console.log(`The transaction id is:`, callTx.id); 52 | console.log(`Waiting transaction be confirmed`); 53 | const confirmedTxn = await callTx.confirm(callTx.id); 54 | 55 | console.log(`The transaction status is:`); 56 | console.log(JSON.stringify(confirmedTxn.receipt)); 57 | 58 | } catch (err) { 59 | console.log(err); 60 | } 61 | } 62 | 63 | main(); 64 | -------------------------------------------------------------------------------- /example/zrc1/burn.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'burn', 29 | [ 30 | { 31 | vname: 'token_id', 32 | type: 'Uint256', 33 | value: '1', 34 | } 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | 45 | // check the pending status 46 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 47 | console.log(`Pending status is: `); 48 | console.log(pendingStatus.result); 49 | 50 | // process confirm 51 | console.log(`The transaction id is:`, callTx.id); 52 | console.log(`Waiting transaction be confirmed`); 53 | const confirmedTxn = await callTx.confirm(callTx.id); 54 | 55 | console.log(`The transaction status is:`); 56 | console.log(confirmedTxn.receipt); 57 | 58 | } catch (err) { 59 | console.log(err); 60 | } 61 | } 62 | 63 | main(); 64 | -------------------------------------------------------------------------------- /example/zrc1/configure_minter.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'configureMinter', 29 | [ 30 | { 31 | vname: 'minter', 32 | type: 'ByStr20', 33 | value: '0xad7a7a32f0718c4e9718faf4b5f8c7c86ffa7fe2', 34 | }, 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | 45 | // check the pending status 46 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 47 | console.log(`Pending status is: `); 48 | console.log(pendingStatus.result); 49 | 50 | // process confirm 51 | console.log(`The transaction id is:`, callTx.id); 52 | console.log(`Waiting transaction be confirmed`); 53 | const confirmedTxn = await callTx.confirm(callTx.id); 54 | 55 | console.log(`The transaction status is:`); 56 | console.log(JSON.stringify(confirmedTxn.receipt)); 57 | 58 | } catch (err) { 59 | console.log(err); 60 | } 61 | } 62 | 63 | main(); 64 | -------------------------------------------------------------------------------- /example/zrc1/deploy.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {Long, bytes, units} = require('@zilliqa-js/util'); 3 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 4 | const {getAddressFromPrivateKey} = require('@zilliqa-js/crypto'); 5 | 6 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 7 | 8 | 9 | async function main() { 10 | const CHAIN_ID = 333; 11 | const MSG_VERSION = 1; 12 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 13 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 14 | zilliqa.wallet.addByPrivateKey( 15 | privkey 16 | ); 17 | const address = getAddressFromPrivateKey(privkey); 18 | console.log("Your account address is:"); 19 | console.log(`${address}`); 20 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 21 | 22 | console.log("start to deploy zrc2: "); 23 | const code = fs.readFileSync("nonfungible-token.scilla").toString(); 24 | console.log("contract code is: "); 25 | console.log(code); 26 | const init = [ 27 | // this parameter is mandatory for all init arrays 28 | { 29 | vname: "_scilla_version", 30 | type: "Uint32", 31 | value: "0" 32 | }, 33 | { 34 | vname: "contract_owner", 35 | type: "ByStr20", 36 | value: `${address}` 37 | }, 38 | { 39 | vname: "name", 40 | type: "String", 41 | value: `USDT` 42 | }, 43 | { 44 | vname: "symbol", 45 | type: "String", 46 | value: `USDT` 47 | } 48 | ]; 49 | console.log("init json is: "); 50 | console.log(JSON.stringify(init)); 51 | const contract = zilliqa.contracts.new(code, init); 52 | try { 53 | const [deployTx, nft] = await contract.deployWithoutConfirm({ 54 | version: VERSION, 55 | gasPrice: myGasPrice, 56 | gasLimit: Long.fromNumber(40000) 57 | }, false); 58 | 59 | if (nft.error) { 60 | console.error(nft.error); 61 | return; 62 | } 63 | // check the pending status 64 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(deployTx.id); 65 | console.log(`Pending status is: `); 66 | console.log(pendingStatus.result); 67 | 68 | // process confirm 69 | console.log(`The transaction id is:`, deployTx.id); 70 | console.log(`Waiting transaction be confirmed`); 71 | const confirmedTxn = await deployTx.confirm(deployTx.id); 72 | 73 | // Introspect the state of the underlying transaction 74 | console.log(`Deployment Transaction ID: ${deployTx.id}`); 75 | 76 | // Get the deployed contract address 77 | console.log("The contract address is:"); 78 | console.log(nft.address); 79 | } catch (e) { 80 | console.error(e); 81 | } 82 | 83 | } 84 | 85 | main(); -------------------------------------------------------------------------------- /example/zrc1/get_approved.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'getApproved', 29 | [ 30 | { 31 | vname: 'token_id', 32 | type: 'Uint256', 33 | value: '2', 34 | } 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | 45 | // check the pending status 46 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 47 | console.log(`Pending status is: `); 48 | console.log(pendingStatus.result); 49 | 50 | // process confirm 51 | console.log(`The transaction id is:`, callTx.id); 52 | console.log(`Waiting transaction be confirmed`); 53 | const confirmedTxn = await callTx.confirm(callTx.id); 54 | 55 | console.log(`The transaction status is:`); 56 | console.log(JSON.stringify(confirmedTxn.receipt)); 57 | 58 | } catch (err) { 59 | console.log(err); 60 | } 61 | } 62 | 63 | main(); 64 | -------------------------------------------------------------------------------- /example/zrc1/get_token_uri.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'getTokenURI', 29 | [ 30 | { 31 | vname: 'token_id', 32 | type: 'Uint256', 33 | value: '2', 34 | } 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | 45 | // check the pending status 46 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 47 | console.log(`Pending status is: `); 48 | console.log(pendingStatus.result); 49 | 50 | // process confirm 51 | console.log(`The transaction id is:`, callTx.id); 52 | console.log(`Waiting transaction be confirmed`); 53 | const confirmedTxn = await callTx.confirm(callTx.id); 54 | 55 | console.log(`The transaction status is:`); 56 | console.log(JSON.stringify(confirmedTxn.receipt)); 57 | 58 | } catch (err) { 59 | console.log(err); 60 | } 61 | } 62 | 63 | main(); 64 | -------------------------------------------------------------------------------- /example/zrc1/is_approved_for_all.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'isApprovedForAll', 29 | [ 30 | { 31 | vname: 'token_owner', 32 | type: 'ByStr20', 33 | value: `${address}`, 34 | }, 35 | { 36 | vname: 'operator', 37 | type: 'ByStr20', 38 | value: '0xad7a7a32f0718c4e9718faf4b5f8c7c86ffa7fe2', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | 50 | // check the pending status 51 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 52 | console.log(`Pending status is: `); 53 | console.log(pendingStatus.result); 54 | 55 | // process confirm 56 | console.log(`The transaction id is:`, callTx.id); 57 | console.log(`Waiting transaction be confirmed`); 58 | const confirmedTxn = await callTx.confirm(callTx.id); 59 | 60 | console.log(`The transaction status is:`); 61 | console.log(JSON.stringify(confirmedTxn.receipt)); 62 | 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /example/zrc1/is_owner.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'isOwner', 29 | [ 30 | { 31 | vname: 'token_id', 32 | type: 'Uint256', 33 | value: '2', 34 | }, 35 | { 36 | vname: 'address', 37 | type: 'ByStr20', 38 | value: `${address}`, 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | 50 | // check the pending status 51 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 52 | console.log(`Pending status is: `); 53 | console.log(pendingStatus.result); 54 | 55 | // process confirm 56 | console.log(`The transaction id is:`, callTx.id); 57 | console.log(`Waiting transaction be confirmed`); 58 | const confirmedTxn = await callTx.confirm(callTx.id); 59 | 60 | console.log(`The transaction status is:`); 61 | console.log(JSON.stringify(confirmedTxn.receipt)); 62 | 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /example/zrc1/mint.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'Mint', 29 | [ 30 | { 31 | vname: 'to', 32 | type: 'ByStr20', 33 | value: `${address}`, 34 | }, 35 | { 36 | vname: 'token_uri', 37 | type: 'String', 38 | value: 'some_token_uri', 39 | } 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | 50 | // check the pending status 51 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 52 | console.log(`Pending status is: `); 53 | console.log(pendingStatus.result); 54 | 55 | // process confirm 56 | console.log(`The transaction id is:`, callTx.id); 57 | console.log(`Waiting transaction be confirmed`); 58 | const confirmedTxn = await callTx.confirm(callTx.id); 59 | 60 | console.log(`The transaction status is:`); 61 | console.log(confirmedTxn.receipt); 62 | 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /example/zrc1/name.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'name', 29 | [], 30 | { 31 | // amount, gasPrice and gasLimit must be explicitly provided 32 | version: VERSION, 33 | amount: new BN(0), 34 | gasPrice: myGasPrice, 35 | gasLimit: Long.fromNumber(10000), 36 | } 37 | ); 38 | 39 | // check the pending status 40 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 41 | console.log(`Pending status is: `); 42 | console.log(pendingStatus.result); 43 | 44 | // process confirm 45 | console.log(`The transaction id is:`, callTx.id); 46 | console.log(`Waiting transaction be confirmed`); 47 | const confirmedTxn = await callTx.confirm(callTx.id); 48 | 49 | console.log(`The transaction status is:`); 50 | console.log(JSON.stringify(confirmedTxn.receipt)); 51 | 52 | } catch (err) { 53 | console.log(err); 54 | } 55 | } 56 | 57 | main(); 58 | -------------------------------------------------------------------------------- /example/zrc1/set_approve_for_all.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'setApprovalForAll', 29 | [ 30 | { 31 | vname: 'to', 32 | type: 'ByStr20', 33 | value: '0xad7a7a32f0718c4e9718faf4b5f8c7c86ffa7fe2', 34 | }, 35 | { 36 | vname: 'approved', 37 | type: 'Bool', 38 | value: { 39 | argtypes: [], 40 | arguments: [], 41 | constructor: 'False', 42 | }, 43 | }, 44 | ], 45 | { 46 | // amount, gasPrice and gasLimit must be explicitly provided 47 | version: VERSION, 48 | amount: new BN(0), 49 | gasPrice: myGasPrice, 50 | gasLimit: Long.fromNumber(10000), 51 | } 52 | ); 53 | 54 | // check the pending status 55 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 56 | console.log(`Pending status is: `); 57 | console.log(pendingStatus.result); 58 | 59 | // process confirm 60 | console.log(`The transaction id is:`, callTx.id); 61 | console.log(`Waiting transaction be confirmed`); 62 | const confirmedTxn = await callTx.confirm(callTx.id); 63 | 64 | console.log(`The transaction status is:`); 65 | console.log(JSON.stringify(confirmedTxn.receipt)); 66 | 67 | } catch (err) { 68 | console.log(err); 69 | } 70 | } 71 | 72 | main(); 73 | -------------------------------------------------------------------------------- /example/zrc1/symbol.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'symbol', 29 | [], 30 | { 31 | // amount, gasPrice and gasLimit must be explicitly provided 32 | version: VERSION, 33 | amount: new BN(0), 34 | gasPrice: myGasPrice, 35 | gasLimit: Long.fromNumber(10000), 36 | } 37 | ); 38 | 39 | // check the pending status 40 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 41 | console.log(`Pending status is: `); 42 | console.log(pendingStatus.result); 43 | 44 | // process confirm 45 | console.log(`The transaction id is:`, callTx.id); 46 | console.log(`Waiting transaction be confirmed`); 47 | const confirmedTxn = await callTx.confirm(callTx.id); 48 | 49 | console.log(`The transaction status is:`); 50 | console.log(JSON.stringify(confirmedTxn.receipt)); 51 | 52 | } catch (err) { 53 | console.log(err); 54 | } 55 | } 56 | 57 | main(); 58 | -------------------------------------------------------------------------------- /example/zrc1/total_supply.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'totalSupply', 29 | [], 30 | { 31 | // amount, gasPrice and gasLimit must be explicitly provided 32 | version: VERSION, 33 | amount: new BN(0), 34 | gasPrice: myGasPrice, 35 | gasLimit: Long.fromNumber(10000), 36 | } 37 | ); 38 | 39 | // check the pending status 40 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 41 | console.log(`Pending status is: `); 42 | console.log(pendingStatus.result); 43 | 44 | // process confirm 45 | console.log(`The transaction id is:`, callTx.id); 46 | console.log(`Waiting transaction be confirmed`); 47 | const confirmedTxn = await callTx.confirm(callTx.id); 48 | 49 | console.log(`The transaction status is:`); 50 | console.log(JSON.stringify(confirmedTxn.receipt)); 51 | 52 | } catch (err) { 53 | console.log(err); 54 | } 55 | } 56 | 57 | main(); 58 | -------------------------------------------------------------------------------- /example/zrc1/transfer.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'transfer', 29 | [ 30 | { 31 | vname: 'to', 32 | type: 'ByStr20', 33 | value: '0xad7a7a32f0718c4e9718faf4b5f8c7c86ffa7fe2', 34 | }, 35 | { 36 | vname: 'token_id', 37 | type: 'Uint256', 38 | value: '2', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | 50 | // check the pending status 51 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 52 | console.log(`Pending status is: `); 53 | console.log(pendingStatus.result); 54 | 55 | // process confirm 56 | console.log(`The transaction id is:`, callTx.id); 57 | console.log(`Waiting transaction be confirmed`); 58 | const confirmedTxn = await callTx.confirm(callTx.id); 59 | 60 | console.log(`The transaction status is:`); 61 | console.log(JSON.stringify(confirmedTxn.receipt)); 62 | 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /example/zrc1/transfer_from.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const nftAddr = toBech32Address("79d662e621c08521a20f80e953417d981ddef0a6"); 25 | try { 26 | const contract = zilliqa.contracts.at(nftAddr); 27 | const callTx = await contract.callWithoutConfirm( 28 | 'transferFrom', 29 | [ 30 | { 31 | vname: 'to', 32 | type: 'ByStr20', 33 | value: '0xad7a7a32f0718c4e9718faf4b5f8c7c86ffa7fe2', 34 | }, 35 | { 36 | vname: 'token_id', 37 | type: 'Uint256', 38 | value: '2', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | 50 | // check the pending status 51 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(callTx.id); 52 | console.log(`Pending status is: `); 53 | console.log(pendingStatus.result); 54 | 55 | // process confirm 56 | console.log(`The transaction id is:`, callTx.id); 57 | console.log(`Waiting transaction be confirmed`); 58 | const confirmedTxn = await callTx.confirm(callTx.id); 59 | 60 | console.log(`The transaction status is:`); 61 | console.log(JSON.stringify(confirmedTxn.receipt)); 62 | 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /example/zrc2/auth_operator.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'AuthorizeOperator', 29 | [ 30 | { 31 | vname: 'operator', 32 | type: 'ByStr20', 33 | value: '0xF9814DFAF5b817b6Ccd2993d94348cC77b354575', 34 | }, 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | console.log(JSON.stringify(callTx.receipt, null, 4)); 45 | 46 | } catch (err) { 47 | console.log(err); 48 | } 49 | } 50 | 51 | main(); 52 | -------------------------------------------------------------------------------- /example/zrc2/burn.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'Burn', 29 | [ 30 | { 31 | vname: 'burn_account', 32 | type: 'ByStr20', 33 | value: `${address}`, 34 | }, 35 | { 36 | vname: 'amount', 37 | type: 'Uint128', 38 | value: '10', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | console.log(JSON.stringify(callTx.receipt, null, 4)); 50 | 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /example/zrc2/decrease_allowance.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'DecreaseAllowance', 29 | [ 30 | { 31 | vname: 'spender', 32 | type: 'ByStr20', 33 | value: "0xF9814DFAF5b817b6Ccd2993d94348cC77b354575", 34 | }, 35 | { 36 | vname: 'amount', 37 | type: 'Uint128', 38 | value: "10", 39 | } 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | console.log(JSON.stringify(callTx.receipt, null, 4)); 50 | 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /example/zrc2/deploy.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {Long, bytes, units} = require('@zilliqa-js/util'); 3 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 4 | const {getAddressFromPrivateKey} = require('@zilliqa-js/crypto'); 5 | 6 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 7 | 8 | 9 | async function main() { 10 | const CHAIN_ID = 333; 11 | const MSG_VERSION = 1; 12 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 13 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 14 | zilliqa.wallet.addByPrivateKey( 15 | privkey 16 | ); 17 | const address = getAddressFromPrivateKey(privkey); 18 | console.log("Your account address is:"); 19 | console.log(`${address}`); 20 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 21 | 22 | console.log("start to deploy zrc2: "); 23 | const code = fs.readFileSync("../../reference-contracts/FungibleToken-Operator.scilla").toString(); 24 | console.log("contract code is: "); 25 | console.log(code); 26 | const init = [ 27 | // this parameter is mandatory for all init arrays 28 | { 29 | vname: "_scilla_version", 30 | type: "Uint32", 31 | value: "0" 32 | }, 33 | { 34 | vname: "contract_owner", 35 | type: "ByStr20", 36 | value: `${address}` 37 | }, 38 | { 39 | vname: "name", 40 | type: "String", 41 | value: `USDT` 42 | }, 43 | { 44 | vname: "symbol", 45 | type: "String", 46 | value: `USDT` 47 | }, 48 | { 49 | vname: "decimals", 50 | type: "Uint32", 51 | value: `2` 52 | }, 53 | { 54 | vname: "default_operators", 55 | type: "List ByStr20", 56 | value: ["0x501A70ffEAA3F31C1caccE3479e74713546BAA44", "0xBFe2445408C51CD8Ee6727541195b02c891109ee", "0x428A2aA43456FE7fd2De66E48C1fBf372eC10eAE"] 57 | }, 58 | { 59 | vname: "init_supply", 60 | type: "Uint128", 61 | value: `100000000` 62 | } 63 | ]; 64 | console.log("init json is: "); 65 | console.log(JSON.stringify(init)); 66 | const contract = zilliqa.contracts.new(code, init); 67 | try { 68 | const [deployTx, ftoken] = await contract.deployWithoutConfirm({ 69 | version: VERSION, 70 | gasPrice: myGasPrice, 71 | gasLimit: Long.fromNumber(40000) 72 | }, false); 73 | 74 | if (ftoken.error) { 75 | console.error(ftoken.error); 76 | return; 77 | } 78 | // check the pending status 79 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(deployTx.id); 80 | console.log(`Pending status is: `); 81 | console.log(pendingStatus.result); 82 | 83 | // process confirm 84 | console.log(`The transaction id is:`, deployTx.id); 85 | console.log(`Waiting transaction be confirmed`); 86 | const confirmedTxn = await deployTx.confirm(deployTx.id); 87 | 88 | // Introspect the state of the underlying transaction 89 | console.log(`Deployment Transaction ID: ${deployTx.id}`); 90 | 91 | // Get the deployed contract address 92 | console.log("The contract address is:"); 93 | console.log(ftoken.address); 94 | } catch (e) { 95 | console.error(e); 96 | } 97 | 98 | } 99 | 100 | main(); -------------------------------------------------------------------------------- /example/zrc2/increase_allowance.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'IncreaseAllowance', 29 | [ 30 | { 31 | vname: 'spender', 32 | type: 'ByStr20', 33 | value: "0xF9814DFAF5b817b6Ccd2993d94348cC77b354575", 34 | }, 35 | { 36 | vname: 'amount', 37 | type: 'Uint128', 38 | value: "100", 39 | } 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | console.log(JSON.stringify(callTx.receipt, null, 4)); 50 | 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /example/zrc2/is_operator_for.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'IsOperatorFor', 29 | [ 30 | { 31 | vname: 'token_owner', 32 | type: 'ByStr20', 33 | value: `${address}`, 34 | }, 35 | { 36 | vname: 'operator', 37 | type: 'ByStr20', 38 | value: '0x501A70ffEAA3F31C1caccE3479e74713546BAA44', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | console.log(JSON.stringify(callTx.receipt, null, 4)); 50 | 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /example/zrc2/mint.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'Mint', 29 | [ 30 | { 31 | vname: 'recipient', 32 | type: 'ByStr20', 33 | value: `${address}`, 34 | }, 35 | { 36 | vname: 'amount', 37 | type: 'Uint128', 38 | value: '100000000', 39 | }, 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | console.log(JSON.stringify(callTx.receipt, null, 4)); 50 | 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /example/zrc2/operator_send.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '534b41b12c3c14e282279da256d85a9ce456e1d8cea4e2ef5c49c71e321b3eac'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'OperatorSend', 29 | [ 30 | { 31 | vname: 'from', 32 | type: 'ByStr20', 33 | value: "0x501A70ffEAA3F31C1caccE3479e74713546BAA44", 34 | }, 35 | { 36 | vname: 'to', 37 | type: 'ByStr20', 38 | value: "0xBFe2445408C51CD8Ee6727541195b02c891109ee", 39 | }, 40 | { 41 | vname: 'amount', 42 | type: 'Uint128', 43 | value: '10', 44 | }, 45 | ], 46 | { 47 | // amount, gasPrice and gasLimit must be explicitly provided 48 | version: VERSION, 49 | amount: new BN(0), 50 | gasPrice: myGasPrice, 51 | gasLimit: Long.fromNumber(10000), 52 | } 53 | ); 54 | console.log(JSON.stringify(callTx.receipt, null, 4)); 55 | 56 | } catch (err) { 57 | console.log(err); 58 | } 59 | } 60 | 61 | main(); 62 | -------------------------------------------------------------------------------- /example/zrc2/revoke_operator.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'RevokeOperator', 29 | [ 30 | { 31 | vname: 'operator', 32 | type: 'ByStr20', 33 | value: '0xF9814DFAF5b817b6Ccd2993d94348cC77b354575', 34 | }, 35 | ], 36 | { 37 | // amount, gasPrice and gasLimit must be explicitly provided 38 | version: VERSION, 39 | amount: new BN(0), 40 | gasPrice: myGasPrice, 41 | gasLimit: Long.fromNumber(10000), 42 | } 43 | ); 44 | console.log(JSON.stringify(callTx.receipt, null, 4)); 45 | 46 | } catch (err) { 47 | console.log(err); 48 | } 49 | } 50 | 51 | main(); 52 | -------------------------------------------------------------------------------- /example/zrc2/transfer.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'Transfer', 29 | [ 30 | { 31 | vname: 'to', 32 | type: 'ByStr20', 33 | value: "0xBFe2445408C51CD8Ee6727541195b02c891109ee", 34 | }, 35 | { 36 | vname: 'amount', 37 | type: 'Uint128', 38 | value: "100", 39 | } 40 | ], 41 | { 42 | // amount, gasPrice and gasLimit must be explicitly provided 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: myGasPrice, 46 | gasLimit: Long.fromNumber(10000), 47 | } 48 | ); 49 | console.log(JSON.stringify(callTx.receipt, null, 4)); 50 | 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /example/zrc2/transfer_from.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '534b41b12c3c14e282279da256d85a9ce456e1d8cea4e2ef5c49c71e321b3eac'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 22 | 23 | 24 | const ftAddr = toBech32Address("509ae6e5d91cee3c6571dcd04aa08288a29d563a"); 25 | try { 26 | const contract = zilliqa.contracts.at(ftAddr); 27 | const callTx = await contract.call( 28 | 'TransferFrom', 29 | [ 30 | { 31 | vname: 'from', 32 | type: 'ByStr20', 33 | value: "0x501A70ffEAA3F31C1caccE3479e74713546BAA44", 34 | }, 35 | { 36 | vname: 'to', 37 | type: 'ByStr20', 38 | value: "0xBFe2445408C51CD8Ee6727541195b02c891109ee", 39 | }, 40 | { 41 | vname: 'amount', 42 | type: 'Uint128', 43 | value: "100", 44 | } 45 | ], 46 | { 47 | // amount, gasPrice and gasLimit must be explicitly provided 48 | version: VERSION, 49 | amount: new BN(0), 50 | gasPrice: myGasPrice, 51 | gasLimit: Long.fromNumber(10000), 52 | } 53 | ); 54 | console.log(JSON.stringify(callTx.receipt, null, 4)); 55 | 56 | } catch (err) { 57 | console.log(err); 58 | } 59 | } 60 | 61 | main(); 62 | -------------------------------------------------------------------------------- /example/zrc3/deploy.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {Long, bytes, units} = require('@zilliqa-js/util'); 3 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 4 | const {getAddressFromPrivateKey} = require('@zilliqa-js/crypto'); 5 | 6 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 7 | 8 | 9 | async function main() { 10 | const CHAIN_ID = 333; 11 | const MSG_VERSION = 1; 12 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 13 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 14 | zilliqa.wallet.addByPrivateKey( 15 | privkey 16 | ); 17 | const address = getAddressFromPrivateKey(privkey); 18 | console.log("Your account address is:"); 19 | console.log(`${address}`); 20 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 21 | 22 | console.log("start to deploy zrc3: "); 23 | const code = fs.readFileSync("MetaFungibleToken.scilla").toString(); 24 | console.log("contract code is: "); 25 | console.log(code); 26 | const init = [ 27 | // this parameter is mandatory for all init arrays 28 | { 29 | vname: "_scilla_version", 30 | type: "Uint32", 31 | value: "0" 32 | }, 33 | { 34 | vname: "contract_owner", 35 | type: "ByStr20", 36 | value: `${address}` 37 | }, 38 | { 39 | vname: "name", 40 | type: "String", 41 | value: `RBB` 42 | }, 43 | { 44 | vname: "symbol", 45 | type: "String", 46 | value: `RBB` 47 | }, 48 | { 49 | vname: "decimals", 50 | type: "Uint32", 51 | value: `2` 52 | }, 53 | { 54 | vname: "init_supply", 55 | type: "Uint128", 56 | value: `100000000` 57 | } 58 | ]; 59 | console.log("init json is: "); 60 | console.log(JSON.stringify(init)); 61 | const contract = zilliqa.contracts.new(code, init); 62 | try { 63 | const [deployTx, ftoken] = await contract.deployWithoutConfirm({ 64 | version: VERSION, 65 | gasPrice: myGasPrice, 66 | gasLimit: Long.fromNumber(40000) 67 | }, false); 68 | 69 | if (ftoken.error) { 70 | console.error(ftoken.error); 71 | return; 72 | } 73 | // check the pending status 74 | const pendingStatus = await zilliqa.blockchain.getPendingTxn(deployTx.id); 75 | console.log(`Pending status is: `); 76 | console.log(pendingStatus.result); 77 | 78 | // process confirm 79 | console.log(`The transaction id is:`, deployTx.id); 80 | console.log(`Waiting transaction be confirmed`); 81 | const confirmedTxn = await deployTx.confirm(deployTx.id); 82 | 83 | // Introspect the state of the underlying transaction 84 | console.log(`Deployment Transaction ID: ${deployTx.id}`); 85 | 86 | // Get the deployed contract address 87 | console.log("The contract address is:"); 88 | console.log(ftoken.address); 89 | } catch (e) { 90 | console.error(e); 91 | } 92 | 93 | } 94 | 95 | main(); -------------------------------------------------------------------------------- /example/zrc3/metatransfer.js: -------------------------------------------------------------------------------- 1 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 2 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 3 | const { 4 | getAddressFromPrivateKey, 5 | getPubKeyFromPrivateKey, 6 | sign, 7 | toBech32Address, 8 | fromBech32Address, 9 | } = require('@zilliqa-js/crypto'); 10 | 11 | var hash = require('hash.js') 12 | 13 | async function metatransfer() { 14 | const zilliqa = new Zilliqa('https://api.zilliqa.com'); 15 | const sender_zilliqa = new Zilliqa('https://api.zilliqa.com'); 16 | const CHAIN_ID = 1; 17 | const MSG_VERSION = 1; 18 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 19 | privkey = '3375F915F3F9AE35E6B301B7670F53AD1A5BE15D8221EC7FD5E503F21D3450C8'; 20 | zilliqa.wallet.addByPrivateKey( 21 | privkey 22 | ); 23 | const alice = getAddressFromPrivateKey(privkey); //alice is the sender 24 | const bob = "114f77519e8a463d25fc1a399870ed0dcb275ce3" //bob recieves the metatransaction 25 | sender_zilliqa.wallet.addByPrivateKey("07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9"); 26 | console.log("bob: ") 27 | console.log(`${bob}`); 28 | 29 | const pubkey = getPubKeyFromPrivateKey(privkey); 30 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 31 | 32 | //Signature Parameters calculation 33 | const amount = 2; 34 | const nonce = 13; 35 | const fee = 2; 36 | const contractAddr = "5ff3cae12c97850f8ca3db2d921ead12690e38c3"; 37 | 38 | const amount_bn = new BN(amount) 39 | const nonce_bn = new BN(nonce) 40 | const fee_bn = new BN(fee) 41 | const uint_amt = Uint8Array.from(amount_bn.toArrayLike(Buffer, undefined, 16)) 42 | const uint_nonce = Uint8Array.from(nonce_bn.toArrayLike(Buffer, undefined, 16)) 43 | const uint_fee = Uint8Array.from(fee_bn.toArrayLike(Buffer, undefined, 16)) 44 | 45 | const to_hash = hash.sha256().update(bytes.hexToByteArray(bob)).digest('hex') 46 | console.log("to_hash: ") 47 | console.log(`${to_hash}`); 48 | 49 | const amount_hash = hash.sha256().update(uint_amt).digest('hex') 50 | console.log("amount_hash: ") 51 | console.log(`${amount_hash}`); 52 | 53 | const contract_hash = hash.sha256().update(bytes.hexToByteArray(contractAddr)).digest('hex') 54 | console.log("contract_hash: ") 55 | console.log(`${contract_hash}`); 56 | 57 | const fee_hash = hash.sha256().update(uint_fee).digest('hex') 58 | console.log("fee_hash: ") 59 | console.log(`${fee_hash}`); 60 | 61 | const nonce_hash = hash.sha256().update(uint_nonce).digest('hex') 62 | console.log("nonce_hash: ") 63 | console.log(`${nonce_hash}`); 64 | 65 | const msg_buf = Buffer.from(to_hash + amount_hash + contract_hash + fee_hash + nonce_hash, 'hex') 66 | const sig = sign(msg_buf, privkey, pubkey) 67 | console.log("pubkey: ") 68 | console.log(`0x` + `${pubkey}`); 69 | console.log("privkey: ") 70 | console.log(`0x` + `${privkey}`); 71 | console.log("buf_message: ") 72 | console.log(`${msg_buf}`); 73 | console.log("sig: ") 74 | console.log(`0x` + `${sig}`); 75 | 76 | try { 77 | const contract = sender_zilliqa.contracts.at(contractAddr); 78 | const callTx = await contract.call( 79 | 'ChequeSend', 80 | [ 81 | { 82 | vname: 'pubkey', 83 | type: 'ByStr33', 84 | value: `0x` + `${pubkey}`, 85 | }, 86 | { 87 | vname: 'to', 88 | type: 'ByStr20', 89 | value: `0x` + `${bob}`, 90 | }, 91 | { 92 | vname: 'amount', 93 | type: 'Uint128', 94 | value: `${amount}`, 95 | }, 96 | { 97 | vname: 'fee', 98 | type: 'Uint128', 99 | value: `${fee}`, 100 | }, 101 | { 102 | vname: 'nonce', 103 | type: 'Uint128', 104 | value: `${nonce}`, 105 | }, 106 | { 107 | vname: 'signature', 108 | type: 'ByStr64', 109 | value: `0x` + `${sig}`, 110 | }, 111 | ], 112 | { 113 | // amount, gasPrice and gasLimit must be explicitly provided 114 | version: VERSION, 115 | amount: new BN(0), 116 | gasPrice: myGasPrice, 117 | gasLimit: Long.fromNumber(10000), 118 | } 119 | ); 120 | console.log(JSON.stringify(callTx.receipt, null, 4)); 121 | 122 | } catch (err) { 123 | console.log(err); 124 | } 125 | } 126 | 127 | metatransfer(); 128 | -------------------------------------------------------------------------------- /example/zrc3/transfer.js: -------------------------------------------------------------------------------- 1 | const {BN, Long, bytes, units} = require('@zilliqa-js/util'); 2 | const {Zilliqa} = require('@zilliqa-js/zilliqa'); 3 | const { 4 | toBech32Address, 5 | getAddressFromPrivateKey, 6 | } = require('@zilliqa-js/crypto'); 7 | 8 | 9 | async function main() { 10 | const zilliqa = new Zilliqa('https://dev-api.zilliqa.com'); 11 | const CHAIN_ID = 333; 12 | const MSG_VERSION = 1; 13 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 14 | privkey = '07e0b1d1870a0ba1b60311323cb9c198d6f6193b2219381c189afab3f5ac41a9'; 15 | zilliqa.wallet.addByPrivateKey( 16 | privkey 17 | ); 18 | const address = getAddressFromPrivateKey(privkey); 19 | const recipient = getAddressFromPrivateKey("3375F915F3F9AE35E6B301B7670F53AD1A5BE15D8221EC7FD5E503F21D3450C8") //alice in metatransfer.js 20 | console.log("Your account address is:"); 21 | console.log(`${address}`); 22 | const myGasPrice = units.toQa('2000', units.Units.Li); // Gas Price that will be used by all transactions 23 | 24 | 25 | const ftAddr = "0b1384bf248f493226fdd1981b9ea56d6c94424d"; 26 | try { 27 | const contract = zilliqa.contracts.at(ftAddr); 28 | const callTx = await contract.call( 29 | 'Transfer', 30 | [ 31 | { 32 | vname: 'to', 33 | type: 'ByStr20', 34 | value: `${recipient}`, 35 | }, 36 | { 37 | vname: 'amount', 38 | type: 'Uint128', 39 | value: "10000", 40 | } 41 | ], 42 | { 43 | // amount, gasPrice and gasLimit must be explicitly provided 44 | version: VERSION, 45 | amount: new BN(0), 46 | gasPrice: myGasPrice, 47 | gasLimit: Long.fromNumber(10000), 48 | } 49 | ); 50 | console.log(JSON.stringify(callTx.receipt, null, 4)); 51 | 52 | } catch (err) { 53 | console.log(err); 54 | } 55 | } 56 | 57 | main(); -------------------------------------------------------------------------------- /example/zrc4/README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | The Javascript examples uses [Zilliqa-Javascript-Library](#https://github.com/Zilliqa/Zilliqa-JavaScript-Library). 4 |
5 | Using npm <= 12.9, run `npm install`.
6 | To execute the examples, configure the parameters in the Javascript files and run `node [file_name.js], e.g. node deploy.js`. 7 | 8 | ## Sample Workflow 9 | 10 | An example of using this multisig wallet contract is as follows: 11 | 1. Prepare some accounts and deploy the wallet contract: `node deploy.js` 12 | 2. Add funds to the wallet: `node add_funds.js` 13 | 3. Create a transaction to transfer some funds to a recipient: `node submit_transaction.js` 14 | 4. Sign the transaction from (3): `node sign_transaction.js`; change the private key to simulate different wallet owners approving the transaction. 15 | 5. Check the transactions and signatures' statuses: `node get_transactions.js` 16 | 6. If necessary, revoke the signature from (4): `node revoke_signature.js` 17 | 7. Execute the transaction from (4) once the number of required signatures is reached: `node execute_transaction.js`; can be wallet owners or recipient -------------------------------------------------------------------------------- /example/zrc4/add_funds.js: -------------------------------------------------------------------------------- 1 | /* 2 | * add native funds to the wallet 3 | */ 4 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 5 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 6 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 7 | 8 | // change the following parameters 9 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 10 | const CHAIN_ID = 1; // 333 for zilliqa dev testnet 11 | const PRIVATE_KEY = 'd96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba'; // any private key, don't have to be wallet owners 12 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); // wallet address 13 | const FUND = "10000"; // amount added to this wallet 14 | 15 | const zilliqa = new Zilliqa(API); 16 | const MSG_VERSION = 1; 17 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 18 | const GAS_PRICE = units.toQa('2000', units.Units.Li); 19 | 20 | async function main() { 21 | try { 22 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 23 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 24 | 25 | console.log("Your account address is:"); 26 | console.log(`${address}`); 27 | console.log("------------------------ begin add funds ------------------------\n"); 28 | 29 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 30 | const callTx = await contract.call( 31 | 'AddFunds', 32 | [], 33 | { 34 | version: VERSION, 35 | amount: new BN(`${FUND}`), 36 | gasPrice: GAS_PRICE, 37 | gasLimit: Long.fromNumber(10000) 38 | } 39 | ); 40 | console.log("transaction: %o", callTx.id); 41 | console.log(JSON.stringify(callTx.receipt, null, 4)); 42 | } catch (err) { 43 | console.log(err); 44 | } 45 | console.log("------------------------ end add funds ------------------------\n"); 46 | } 47 | 48 | main(); -------------------------------------------------------------------------------- /example/zrc4/deploy.js: -------------------------------------------------------------------------------- 1 | /* 2 | * deploy multisig wallet contract 3 | * this example deploys a 3-owners requiring 2/3 signatures wallet 4 | */ 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 8 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 9 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 10 | 11 | // change the following parameters 12 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 13 | const CHAIN_ID = 1; // 333 for zilliqa dev testnet 14 | const PRIVATE_KEY = 'e53d1c3edaffc7a7bab5418eb836cf75819a82872b4a1a0f1c7fcf5c3e020b89'; 15 | 16 | const OWNER_ADDR1 = '0xd90f2e538ce0df89c8273cad3b63ec44a3c4ed82'; 17 | const OWNER_ADDR2 = '0x381f4008505e940ad7681ec3468a719060caf796'; 18 | const OWNER_ADDR3 = '0xb028055ea3bc78d759d10663da40d171dec992aa'; 19 | const REQUIRED_SIGNATURES = 2; 20 | 21 | const zilliqa = new Zilliqa(API); 22 | const MSG_VERSION = 1; 23 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 24 | const GAS_PRICE = units.toQa('2000', units.Units.Li); 25 | 26 | const MULTISIG_CONTRACT_PATH = "../../reference-contracts/multisig_wallet.scilla"; 27 | 28 | async function main() { 29 | try { 30 | // use any one of the owners to deploy 31 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 32 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 33 | 34 | console.log("Your account address is:"); 35 | console.log(`${address}`); 36 | console.log("------------------------ begin deploy multisig wallet ------------------------\n"); 37 | console.log(`Deploying proxy contract: ` + MULTISIG_CONTRACT_PATH); 38 | const code = fs.readFileSync(path.join(__dirname, MULTISIG_CONTRACT_PATH), 'ascii'); 39 | 40 | const init = [ 41 | { 42 | vname: "_scilla_version", 43 | type: "Uint32", 44 | value: "0", 45 | }, 46 | { 47 | vname: "owners_list", 48 | type: "List ByStr20", 49 | value: [OWNER_ADDR1, OWNER_ADDR2, OWNER_ADDR3] 50 | }, 51 | { 52 | vname: "required_signatures", 53 | type: "Uint32", 54 | value: `${REQUIRED_SIGNATURES}` 55 | } 56 | ]; 57 | 58 | const contract = zilliqa.contracts.new(code, init); 59 | const [deployTx, multiSigWallet] = await contract.deploy( 60 | { 61 | version: VERSION, 62 | amount: new BN(0), 63 | gasPrice: GAS_PRICE, 64 | gasLimit: Long.fromNumber(25000) 65 | }, 66 | 33, 67 | 1000, 68 | true 69 | ); 70 | 71 | console.log(`Deployment Transaction ID: ${deployTx.id}`); 72 | console.log(`Deployment Transaction Receipt`); 73 | console.log(deployTx.txParams.receipt); 74 | console.log('multisig wallet address: %o', multiSigWallet.address); 75 | 76 | console.log("------------------------ end deploy multisig wallet ------------------------\n"); 77 | } catch (err) { 78 | console.log(err); 79 | } 80 | } 81 | 82 | main(); -------------------------------------------------------------------------------- /example/zrc4/execute_transaction.js: -------------------------------------------------------------------------------- 1 | /* 2 | * execute transaction 3 | * this example deploys a 3-owners requiring 2/3 signatures wallet 4 | */ 5 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 6 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 7 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 8 | 9 | // change the following parameters 10 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 11 | const CHAIN_ID = 1; // 333 for zilliqa dev testnet 12 | const PRIVATE_KEY = '589417286a3213dceb37f8f89bd164c3505a4cec9200c61f7c6db13a30a71b45'; // any of the wallet owners, or the recipient's private key 13 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 14 | const TRANSACTION_ID = '0'; // transaction to be executed 15 | 16 | const zilliqa = new Zilliqa(API); 17 | const MSG_VERSION = 1; 18 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 19 | const GAS_PRICE = units.toQa('2000', units.Units.Li); 20 | 21 | async function main() { 22 | try { 23 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 24 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 25 | 26 | console.log("Your account address is:"); 27 | console.log(`${address}`); 28 | console.log("------------------------ begin execute transaction ------------------------\n"); 29 | 30 | // transaction must first be signed according to the minimal number of requried signatures 31 | // when initialising the contract 32 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 33 | const callTx = await contract.call( 34 | 'ExecuteTransaction', 35 | [ 36 | { 37 | vname: 'transactionId', 38 | type: 'Uint32', 39 | value: `${TRANSACTION_ID}` 40 | } 41 | ], 42 | { 43 | version: VERSION, 44 | amount: new BN(0), 45 | gasPrice: GAS_PRICE, 46 | gasLimit: Long.fromNumber(10000) 47 | } 48 | ); 49 | console.log("transaction: %o", callTx.id); 50 | console.log(JSON.stringify(callTx.receipt, null, 4)); 51 | } catch (err) { 52 | console.log(err); 53 | } 54 | console.log("------------------------ end execute transaction ------------------------\n"); 55 | } 56 | 57 | main(); -------------------------------------------------------------------------------- /example/zrc4/get_balance.js: -------------------------------------------------------------------------------- 1 | /* 2 | * get wallet balance 3 | */ 4 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 5 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 6 | 7 | // change the following parameters 8 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 9 | const PRIVATE_KEY = '589417286a3213dceb37f8f89bd164c3505a4cec9200c61f7c6db13a30a71b45'; // any private key, don't have to be wallet owners 10 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 11 | 12 | const zilliqa = new Zilliqa(API); 13 | 14 | async function main() { 15 | try { 16 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 17 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 18 | 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | console.log("------------------------ begin get balance ------------------------\n"); 22 | 23 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 24 | const state = await contract.getState(); 25 | 26 | console.log("Current wallet: %o - balance: %o", MULTISIG_CONTRACT_ADDR, state._balance); 27 | 28 | } catch (err) { 29 | console.log(err); 30 | } 31 | console.log("------------------------ end get balance ------------------------\n"); 32 | } 33 | 34 | main(); -------------------------------------------------------------------------------- /example/zrc4/get_owners.js: -------------------------------------------------------------------------------- 1 | /* 2 | * get all owners of the wallet 3 | */ 4 | 5 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 6 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 7 | 8 | // change the following parameters 9 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 10 | const PRIVATE_KEY = '589417286a3213dceb37f8f89bd164c3505a4cec9200c61f7c6db13a30a71b45'; // any private key, don't have to be wallet owners 11 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 12 | 13 | const zilliqa = new Zilliqa(API); 14 | 15 | async function main() { 16 | try { 17 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 18 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 19 | 20 | console.log("Your account address is:"); 21 | console.log(`${address}`); 22 | console.log("------------------------ begin get owners ------------------------\n"); 23 | 24 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 25 | const state = await contract.getState(); 26 | 27 | if (Object.keys(state.owners).length === 0) { 28 | console.error("Owners list is empty"); 29 | } else { 30 | console.log("Wallet: %o", MULTISIG_CONTRACT_ADDR); 31 | console.log("Owners: %o", Object.keys(state.owners)); 32 | } 33 | 34 | } catch (err) { 35 | console.log(err); 36 | } 37 | console.log("------------------------ end get owners ------------------------\n"); 38 | } 39 | 40 | main(); -------------------------------------------------------------------------------- /example/zrc4/get_transactions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * get all the transactions and their signatures 3 | */ 4 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 5 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 6 | 7 | // change the following parameters 8 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 9 | const PRIVATE_KEY = '589417286a3213dceb37f8f89bd164c3505a4cec9200c61f7c6db13a30a71b45'; // any private key, don't have to be wallet owners 10 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 11 | 12 | const zilliqa = new Zilliqa(API); 13 | 14 | async function main() { 15 | try { 16 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 17 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 18 | 19 | console.log("Your account address is:"); 20 | console.log(`${address}`); 21 | console.log("------------------------ begin get transactions ------------------------\n"); 22 | 23 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 24 | const state = await contract.getState(); 25 | 26 | console.log("Transactions:"); 27 | console.log(state.transactions); 28 | console.log("\n"); 29 | console.log("Signatures:"); 30 | console.log(state.signatures) 31 | 32 | } catch (err) { 33 | console.log(err); 34 | } 35 | console.log("------------------------ end get transactions ------------------------\n"); 36 | } 37 | 38 | main(); -------------------------------------------------------------------------------- /example/zrc4/revoke_signature.js: -------------------------------------------------------------------------------- 1 | /* 2 | * revoke signature from a transaction 3 | * this example deploys a 3-owners requiring 2/3 signatures wallet 4 | */ 5 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 6 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 7 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 8 | 9 | // change the following parameters 10 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 11 | const CHAIN_ID = 1; // 333 for zilliqa dev testnet 12 | const PRIVATE_KEY = 'd96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba'; // private key of the signee 13 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 14 | const TRANSACTION_ID = '0'; // transaction id to revoke the signature from 15 | 16 | const zilliqa = new Zilliqa(API); 17 | const MSG_VERSION = 1; 18 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 19 | const GAS_PRICE = units.toQa('2000', units.Units.Li); 20 | 21 | async function main() { 22 | try { 23 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 24 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 25 | 26 | console.log("Your account address is:"); 27 | console.log(`${address}`); 28 | console.log("------------------------ begin revoke signature ------------------------\n"); 29 | 30 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 31 | const callTx = await contract.call( 32 | 'RevokeSignature', 33 | [ 34 | { 35 | vname: 'transactionId', 36 | type: 'Uint32', 37 | value: `${TRANSACTION_ID}` 38 | } 39 | ], 40 | { 41 | version: VERSION, 42 | amount: new BN(0), 43 | gasPrice: GAS_PRICE, 44 | gasLimit: Long.fromNumber(10000) 45 | } 46 | ); 47 | console.log("transaction: %o", callTx.id); 48 | console.log(JSON.stringify(callTx.receipt, null, 4)); 49 | } catch (err) { 50 | console.log(err); 51 | } 52 | console.log("------------------------ end revoke signature ------------------------\n"); 53 | } 54 | 55 | main(); -------------------------------------------------------------------------------- /example/zrc4/sign_transaction.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sign transaction 3 | * this example deploys a 3-owners requiring 2/3 signatures wallet 4 | */ 5 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 6 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 7 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 8 | 9 | // change the following parameters 10 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 11 | const CHAIN_ID = 1; // 333 for zilliqa dev testnet 12 | const PRIVATE_KEY = 'd96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba'; // any of the wallet owners 13 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 14 | const TRANSACTION_ID = '2'; // transaction to be signed 15 | 16 | const zilliqa = new Zilliqa(API); 17 | const MSG_VERSION = 1; 18 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 19 | const GAS_PRICE = units.toQa('2000', units.Units.Li); 20 | 21 | async function main() { 22 | try { 23 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 24 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 25 | 26 | console.log("Your account address is:"); 27 | console.log(`${address}`); 28 | console.log("------------------------ begin sign transaction ------------------------\n"); 29 | 30 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 31 | const callTx = await contract.call( 32 | 'SignTransaction', 33 | [ 34 | { 35 | vname: 'transactionId', 36 | type: 'Uint32', 37 | value: `${TRANSACTION_ID}` 38 | } 39 | ], 40 | { 41 | version: VERSION, 42 | amount: new BN(0), 43 | gasPrice: GAS_PRICE, 44 | gasLimit: Long.fromNumber(10000) 45 | } 46 | ); 47 | console.log("transaction: %o", callTx.id); 48 | console.log(JSON.stringify(callTx.receipt, null, 4)); 49 | } catch (err) { 50 | console.log(err); 51 | } 52 | console.log("------------------------ end sign transaction ------------------------\n"); 53 | } 54 | 55 | main(); -------------------------------------------------------------------------------- /example/zrc4/submit_transaction.js: -------------------------------------------------------------------------------- 1 | /* 2 | * submit transaction 3 | * this example deploys a 3-owners requiring 2/3 signatures wallet 4 | */ 5 | const { BN, Long, bytes, units } = require('@zilliqa-js/util'); 6 | const { Zilliqa } = require('@zilliqa-js/zilliqa'); 7 | const { toBech32Address, getAddressFromPrivateKey } = require('@zilliqa-js/crypto'); 8 | 9 | // change the following parameters 10 | const API = 'http://localhost:5555' // https://dev-api.zilliqa.com for zilliqa dev test 11 | const CHAIN_ID = 1; // 333 for zilliqa dev testnet 12 | const PRIVATE_KEY = 'e53d1c3edaffc7a7bab5418eb836cf75819a82872b4a1a0f1c7fcf5c3e020b89'; // any of the wallet owners 13 | const MULTISIG_CONTRACT_ADDR = toBech32Address('0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'); 14 | const RECIPIENT = '0xB9289D96Ee3CC4456ea8911B8a95d5aa939823c1'; // can be wallet address 15 | const TRANSFER_AMOUNT = '1000'; 16 | 17 | const zilliqa = new Zilliqa(API); 18 | const MSG_VERSION = 1; 19 | const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 20 | const GAS_PRICE = units.toQa('2000', units.Units.Li); 21 | 22 | async function main() { 23 | try { 24 | zilliqa.wallet.addByPrivateKey(PRIVATE_KEY); 25 | const address = getAddressFromPrivateKey(PRIVATE_KEY); 26 | 27 | console.log("Your account address is:"); 28 | console.log(`${address}`); 29 | console.log("------------------------ begin submit transaction ------------------------\n"); 30 | 31 | const contract = zilliqa.contracts.at(MULTISIG_CONTRACT_ADDR); 32 | const callTx = await contract.call( 33 | 'SubmitTransaction', 34 | [ 35 | { 36 | vname: 'recipient', 37 | type: 'ByStr20', 38 | value: `${RECIPIENT}` 39 | }, 40 | { 41 | vname: 'amount', 42 | type: 'Uint128', 43 | value: `${TRANSFER_AMOUNT}` 44 | }, 45 | { 46 | vname: 'tag', 47 | type: 'String', 48 | value: 'AddFunds' 49 | } 50 | ], 51 | { 52 | version: VERSION, 53 | amount: new BN(0), 54 | gasPrice: GAS_PRICE, 55 | gasLimit: Long.fromNumber(10000) 56 | } 57 | ); 58 | console.log("transaction: %o", callTx.id); 59 | console.log(JSON.stringify(callTx.receipt, null, 4)); 60 | } catch (err) { 61 | console.log(err); 62 | } 63 | console.log("------------------------ end submit transaction ------------------------\n"); 64 | } 65 | 66 | main(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zrc", 3 | "scripts": { 4 | "run:isolated-server": "docker run --name zrc_local -d -p 5555:5555 --entrypoint isolatedServer zilliqa/zilliqa-isolated-server:a01fe00 -t 5000 -f boot.json -u 0", 5 | "rm:isolated-server": "docker stop zrc_local | xargs docker rm", 6 | "test:zrc": "PORT=5555 jest --verbose --runInBand", 7 | "test": "npm run run:isolated-server; npm run test:zrc && npm run rm:isolated-server", 8 | "format": "npx prettier --write ." 9 | }, 10 | "devDependencies": { 11 | "@types/jest": "^29.5.12", 12 | "@zilliqa-js/zilliqa": "3.5.0", 13 | "jest": "^29.7.0", 14 | "prettier": "^3.2.5", 15 | "@zilliqa-js/scilla-json-utils": "0.2.0", 16 | "ts-jest": "^29.1.2", 17 | "typescript": "^5.4.5" 18 | }, 19 | "type": "module", 20 | "jest": { 21 | "preset": "ts-jest", 22 | "testTimeout": 600000, 23 | "globals": { 24 | "GENESIS_PRIVATE_KEYS": [ 25 | "e53d1c3edaffc7a7bab5418eb836cf75819a82872b4a1a0f1c7fcf5c3e020b89" 26 | ] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /reference-contracts/FungibleToken-Burnable.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | (***************************************************) 4 | (* Associated library *) 5 | (***************************************************) 6 | import IntUtils 7 | library FungibleToken 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let two_msgs = 15 | fun (msg1 : Message) => 16 | fun (msg2 : Message) => 17 | let msgs_tmp = one_msg msg2 in 18 | Cons {Message} msg1 msgs_tmp 19 | 20 | (* Error events *) 21 | type Error = 22 | | CodeIsSender 23 | | CodeInsufficientFunds 24 | | CodeInsufficientAllowance 25 | 26 | let make_error = 27 | fun (result : Error) => 28 | let result_code = 29 | match result with 30 | | CodeIsSender => Int32 -1 31 | | CodeInsufficientFunds => Int32 -2 32 | | CodeInsufficientAllowance => Int32 -3 33 | end 34 | in 35 | { _exception : "Error"; code : result_code } 36 | 37 | let zero = Uint128 0 38 | 39 | (* Dummy user-defined ADT *) 40 | type Unit = 41 | | Unit 42 | 43 | let get_val = 44 | fun (some_val: Option Uint128) => 45 | match some_val with 46 | | Some val => val 47 | | None => zero 48 | end 49 | 50 | (***************************************************) 51 | (* The contract definition *) 52 | (***************************************************) 53 | 54 | contract FungibleToken 55 | ( 56 | contract_owner: ByStr20, 57 | name : String, 58 | symbol: String, 59 | decimals: Uint32, 60 | init_supply : Uint128 61 | ) 62 | 63 | (* Mutable fields *) 64 | 65 | field total_supply : Uint128 = init_supply 66 | 67 | field balances: Map ByStr20 Uint128 68 | = let emp_map = Emp ByStr20 Uint128 in 69 | builtin put emp_map contract_owner init_supply 70 | 71 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 72 | = Emp ByStr20 (Map ByStr20 Uint128) 73 | 74 | (**************************************) 75 | (* Procedures *) 76 | (**************************************) 77 | 78 | procedure ThrowError(err : Error) 79 | e = make_error err; 80 | throw e 81 | end 82 | 83 | procedure IsNotSender(address: ByStr20) 84 | is_sender = builtin eq _sender address; 85 | match is_sender with 86 | | True => 87 | err = CodeIsSender; 88 | ThrowError err 89 | | False => 90 | end 91 | end 92 | 93 | procedure AuthorizedBurnIfSufficientBalance(from: ByStr20, amount: Uint128) 94 | o_get_bal <- balances[from]; 95 | bal = get_val o_get_bal; 96 | can_burn = uint128_le amount bal; 97 | match can_burn with 98 | | True => 99 | (* Subtract amount from from *) 100 | new_balance = builtin sub bal amount; 101 | balances[from] := new_balance; 102 | current_total_supply <- total_supply; 103 | new_total_supply = builtin sub current_total_supply amount; 104 | total_supply := new_total_supply 105 | | False => 106 | err = CodeInsufficientFunds; 107 | ThrowError err 108 | end 109 | end 110 | 111 | procedure AuthorizedMoveIfSufficientBalance(from: ByStr20, to: ByStr20, amount: Uint128) 112 | o_from_bal <- balances[from]; 113 | bal = get_val o_from_bal; 114 | can_do = uint128_le amount bal; 115 | match can_do with 116 | | True => 117 | (* Subtract amount from from and add it to to address *) 118 | new_from_bal = builtin sub bal amount; 119 | balances[from] := new_from_bal; 120 | (* Adds amount to to address *) 121 | get_to_bal <- balances[to]; 122 | new_to_bal = match get_to_bal with 123 | | Some bal => builtin add bal amount 124 | | None => amount 125 | end; 126 | balances[to] := new_to_bal 127 | | False => 128 | (* Balance not sufficient *) 129 | err = CodeInsufficientFunds; 130 | ThrowError err 131 | end 132 | end 133 | 134 | (***************************************) 135 | (* Transitions *) 136 | (***************************************) 137 | 138 | (* @dev: Burn existing tokens. Only owner of token can burn *) 139 | (* @param amount: Number of tokens to be burned. *) 140 | transition Burn(amount: Uint128) 141 | AuthorizedBurnIfSufficientBalance _sender amount; 142 | e = {_eventname: "Burnt"; burner: _sender; burn_account: _sender; amount: amount}; 143 | event e; 144 | msg_to_sender = {_tag : "BurnSuccessCallBack"; _recipient : _sender; _amount : zero; 145 | burner : _sender; amount : amount}; 146 | msgs = one_msg msg_to_sender; 147 | send msgs 148 | end 149 | (* @dev: Increase the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 150 | (* param spender: Address of the designated approved_spender. *) 151 | (* param amount: Number of tokens to be increased as allowance for the approved_spender. *) 152 | transition IncreaseAllowance(spender: ByStr20, amount: Uint128) 153 | IsNotSender spender; 154 | some_current_allowance <- allowances[_sender][spender]; 155 | current_allowance = get_val some_current_allowance; 156 | new_allowance = builtin add current_allowance amount; 157 | allowances[_sender][spender] := new_allowance; 158 | e = {_eventname : "IncreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 159 | event e 160 | end 161 | 162 | (* @dev: Decrease the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 163 | (* param spender: Address of the designated approved_spender. *) 164 | (* param amount: Number of tokens to be decreased as allowance for the approved_spender. *) 165 | transition DecreaseAllowance(spender: ByStr20, amount: Uint128) 166 | IsNotSender spender; 167 | some_current_allowance <- allowances[_sender][spender]; 168 | current_allowance = get_val some_current_allowance; 169 | new_allowance = 170 | let amount_le_allowance = uint128_le amount current_allowance in 171 | match amount_le_allowance with 172 | | True => builtin sub current_allowance amount 173 | | False => zero 174 | end; 175 | allowances[_sender][spender] := new_allowance; 176 | e = {_eventname : "DecreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 177 | event e 178 | end 179 | 180 | (* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) 181 | (* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) 182 | (* @param to: Address of the recipient whose balance is increased. *) 183 | (* @param amount: Amount of tokens to be sent. *) 184 | transition Transfer(to: ByStr20, amount: Uint128) 185 | AuthorizedMoveIfSufficientBalance _sender to amount; 186 | e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; 187 | event e; 188 | (* Prevent sending to a contract address that does not support transfers of token *) 189 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 190 | sender : _sender; recipient : to; amount : amount}; 191 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 192 | sender : _sender; recipient : to; amount : amount}; 193 | msgs = two_msgs msg_to_recipient msg_to_sender; 194 | send msgs 195 | end 196 | 197 | (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) 198 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 199 | (* @param from: Address of the token_owner whose balance is decreased. *) 200 | (* @param to: Address of the recipient whose balance is increased. *) 201 | (* @param amount: Amount of tokens to be transferred. *) 202 | transition TransferFrom(from: ByStr20, to: ByStr20, amount: Uint128) 203 | o_spender_allowed <- allowances[from][_sender]; 204 | allowed = get_val o_spender_allowed; 205 | can_do = uint128_le amount allowed; 206 | match can_do with 207 | | True => 208 | AuthorizedMoveIfSufficientBalance from to amount; 209 | e = {_eventname : "TransferFromSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 210 | event e; 211 | new_allowed = builtin sub allowed amount; 212 | allowances[from][_sender] := new_allowed; 213 | (* Prevent sending to a contract address that does not support transfers of token *) 214 | msg_to_recipient = {_tag: "RecipientAcceptTransferFrom"; _recipient : to; _amount: zero; 215 | initiator: _sender; sender : from; recipient: to; amount: amount}; 216 | msg_to_sender = {_tag: "TransferFromSuccessCallBack"; _recipient: _sender; _amount: zero; 217 | initiator: _sender; sender: from; recipient: to; amount: amount}; 218 | msgs = two_msgs msg_to_recipient msg_to_sender; 219 | send msgs 220 | | False => 221 | err = CodeInsufficientAllowance; 222 | ThrowError err 223 | end 224 | end 225 | -------------------------------------------------------------------------------- /reference-contracts/FungibleToken-Mintable.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | (***************************************************) 4 | (* Associated library *) 5 | (***************************************************) 6 | import IntUtils 7 | library FungibleToken 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let two_msgs = 15 | fun (msg1 : Message) => 16 | fun (msg2 : Message) => 17 | let msgs_tmp = one_msg msg2 in 18 | Cons {Message} msg1 msgs_tmp 19 | 20 | (* Error events *) 21 | type Error = 22 | | CodeIsSender 23 | | CodeInsufficientFunds 24 | | CodeInsufficientAllowance 25 | | CodeNotOwner 26 | 27 | let make_error = 28 | fun (result : Error) => 29 | let result_code = 30 | match result with 31 | | CodeIsSender => Int32 -1 32 | | CodeInsufficientFunds => Int32 -2 33 | | CodeInsufficientAllowance => Int32 -3 34 | | CodeNotOwner => Int32 -4 35 | end 36 | in 37 | { _exception : "Error"; code : result_code } 38 | 39 | let zero = Uint128 0 40 | 41 | (* Dummy user-defined ADT *) 42 | type Unit = 43 | | Unit 44 | 45 | let get_val = 46 | fun (some_val: Option Uint128) => 47 | match some_val with 48 | | Some val => val 49 | | None => zero 50 | end 51 | 52 | (***************************************************) 53 | (* The contract definition *) 54 | (***************************************************) 55 | 56 | contract FungibleToken 57 | ( 58 | contract_owner: ByStr20, 59 | name : String, 60 | symbol: String, 61 | decimals: Uint32, 62 | init_supply : Uint128 63 | ) 64 | 65 | (* Mutable fields *) 66 | 67 | field total_supply : Uint128 = init_supply 68 | 69 | field balances: Map ByStr20 Uint128 70 | = let emp_map = Emp ByStr20 Uint128 in 71 | builtin put emp_map contract_owner init_supply 72 | 73 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 74 | = Emp ByStr20 (Map ByStr20 Uint128) 75 | 76 | (**************************************) 77 | (* Procedures *) 78 | (**************************************) 79 | 80 | procedure ThrowError(err : Error) 81 | e = make_error err; 82 | throw e 83 | end 84 | 85 | procedure IsOwner(address: ByStr20) 86 | is_owner = builtin eq contract_owner address; 87 | match is_owner with 88 | | True => 89 | | False => 90 | err = CodeNotOwner; 91 | ThrowError err 92 | end 93 | end 94 | 95 | procedure IsNotSender(address: ByStr20) 96 | is_sender = builtin eq _sender address; 97 | match is_sender with 98 | | True => 99 | err = CodeIsSender; 100 | ThrowError err 101 | | False => 102 | end 103 | end 104 | 105 | procedure AuthorizedMint(recipient: ByStr20, amount: Uint128) 106 | o_recipient_bal <- balances[recipient]; 107 | bal = get_val o_recipient_bal; 108 | new_balance = builtin add amount bal; 109 | balances[recipient] := new_balance; 110 | current_total_supply <- total_supply; 111 | new_total_supply = builtin add current_total_supply amount; 112 | total_supply := new_total_supply; 113 | e = {_eventname: "Minted"; minter: _sender; recipient: recipient; amount: amount}; 114 | event e 115 | end 116 | 117 | procedure AuthorizedBurnIfSufficientBalance(from: ByStr20, amount: Uint128) 118 | o_get_bal <- balances[from]; 119 | bal = get_val o_get_bal; 120 | can_burn = uint128_le amount bal; 121 | match can_burn with 122 | | True => 123 | (* Subtract amount from from *) 124 | new_balance = builtin sub bal amount; 125 | balances[from] := new_balance; 126 | current_total_supply <- total_supply; 127 | new_total_supply = builtin sub current_total_supply amount; 128 | total_supply := new_total_supply; 129 | e = {_eventname: "Burnt"; burner: _sender; burn_account: from; amount: amount}; 130 | event e 131 | | False => 132 | err = CodeInsufficientFunds; 133 | ThrowError err 134 | end 135 | end 136 | 137 | procedure AuthorizedMoveIfSufficientBalance(from: ByStr20, to: ByStr20, amount: Uint128) 138 | o_from_bal <- balances[from]; 139 | bal = get_val o_from_bal; 140 | can_do = uint128_le amount bal; 141 | match can_do with 142 | | True => 143 | (* Subtract amount from from and add it to to address *) 144 | new_from_bal = builtin sub bal amount; 145 | balances[from] := new_from_bal; 146 | (* Adds amount to to address *) 147 | get_to_bal <- balances[to]; 148 | new_to_bal = match get_to_bal with 149 | | Some bal => builtin add bal amount 150 | | None => amount 151 | end; 152 | balances[to] := new_to_bal 153 | | False => 154 | (* Balance not sufficient *) 155 | err = CodeInsufficientFunds; 156 | ThrowError err 157 | end 158 | end 159 | 160 | (***************************************) 161 | (* Transitions *) 162 | (***************************************) 163 | 164 | (* @dev: Mint new tokens. Only contract_owner can mint. *) 165 | (* @param recipient: Address of the recipient whose balance is to increase. *) 166 | (* @param amount: Number of tokens to be minted. *) 167 | transition Mint(recipient: ByStr20, amount: Uint128) 168 | IsOwner _sender; 169 | AuthorizedMint recipient amount; 170 | (* Prevent sending to a contract address that does not support transfers of token *) 171 | msg_to_recipient = {_tag : "RecipientAcceptMint"; _recipient : recipient; _amount : zero; 172 | minter : _sender; recipient : recipient; amount : amount}; 173 | msg_to_sender = {_tag : "MintSuccessCallBack"; _recipient : _sender; _amount : zero; 174 | minter : _sender; recipient : recipient; amount : amount}; 175 | msgs = two_msgs msg_to_recipient msg_to_sender; 176 | send msgs 177 | end 178 | 179 | (* @dev: Burn existing tokens. Only contract_owner can burn. *) 180 | (* @param burn_account: Address of the token_owner whose balance is to decrease. *) 181 | (* @param amount: Number of tokens to be burned. *) 182 | transition Burn(burn_account: ByStr20, amount: Uint128) 183 | IsOwner _sender; 184 | AuthorizedBurnIfSufficientBalance burn_account amount; 185 | msg_to_sender = {_tag : "BurnSuccessCallBack"; _recipient : _sender; _amount : zero; 186 | burner : _sender; burn_account : burn_account; amount : amount}; 187 | msgs = one_msg msg_to_sender; 188 | send msgs 189 | end 190 | 191 | (* @dev: Increase the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 192 | (* param spender: Address of the designated approved_spender. *) 193 | (* param amount: Number of tokens to be increased as allowance for the approved_spender. *) 194 | transition IncreaseAllowance(spender: ByStr20, amount: Uint128) 195 | IsNotSender spender; 196 | some_current_allowance <- allowances[_sender][spender]; 197 | current_allowance = get_val some_current_allowance; 198 | new_allowance = builtin add current_allowance amount; 199 | allowances[_sender][spender] := new_allowance; 200 | e = {_eventname : "IncreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 201 | event e 202 | end 203 | 204 | (* @dev: Decrease the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 205 | (* param spender: Address of the designated approved_spender. *) 206 | (* param amount: Number of tokens to be decreased as allowance for the approved_spender. *) 207 | transition DecreaseAllowance(spender: ByStr20, amount: Uint128) 208 | IsNotSender spender; 209 | some_current_allowance <- allowances[_sender][spender]; 210 | current_allowance = get_val some_current_allowance; 211 | new_allowance = 212 | let amount_le_allowance = uint128_le amount current_allowance in 213 | match amount_le_allowance with 214 | | True => builtin sub current_allowance amount 215 | | False => zero 216 | end; 217 | allowances[_sender][spender] := new_allowance; 218 | e = {_eventname : "DecreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 219 | event e 220 | end 221 | 222 | (* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) 223 | (* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) 224 | (* @param to: Address of the recipient whose balance is increased. *) 225 | (* @param amount: Amount of tokens to be sent. *) 226 | transition Transfer(to: ByStr20, amount: Uint128) 227 | AuthorizedMoveIfSufficientBalance _sender to amount; 228 | e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; 229 | event e; 230 | (* Prevent sending to a contract address that does not support transfers of token *) 231 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 232 | sender : _sender; recipient : to; amount : amount}; 233 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 234 | sender : _sender; recipient : to; amount : amount}; 235 | msgs = two_msgs msg_to_recipient msg_to_sender; 236 | send msgs 237 | end 238 | 239 | (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) 240 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 241 | (* @param from: Address of the token_owner whose balance is decreased. *) 242 | (* @param to: Address of the recipient whose balance is increased. *) 243 | (* @param amount: Amount of tokens to be transferred. *) 244 | transition TransferFrom(from: ByStr20, to: ByStr20, amount: Uint128) 245 | o_spender_allowed <- allowances[from][_sender]; 246 | allowed = get_val o_spender_allowed; 247 | can_do = uint128_le amount allowed; 248 | match can_do with 249 | | True => 250 | AuthorizedMoveIfSufficientBalance from to amount; 251 | e = {_eventname : "TransferFromSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 252 | event e; 253 | new_allowed = builtin sub allowed amount; 254 | allowances[from][_sender] := new_allowed; 255 | (* Prevent sending to a contract address that does not support transfers of token *) 256 | msg_to_recipient = {_tag: "RecipientAcceptTransferFrom"; _recipient : to; _amount: zero; 257 | initiator: _sender; sender : from; recipient: to; amount: amount}; 258 | msg_to_sender = {_tag: "TransferFromSuccessCallBack"; _recipient: _sender; _amount: zero; 259 | initiator: _sender; sender: from; recipient: to; amount: amount}; 260 | msgs = two_msgs msg_to_recipient msg_to_sender; 261 | send msgs 262 | | False => 263 | err = CodeInsufficientAllowance; 264 | ThrowError err 265 | end 266 | end 267 | -------------------------------------------------------------------------------- /reference-contracts/FungibleToken-Operator.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | (***************************************************) 4 | (* Associated library *) 5 | (***************************************************) 6 | import BoolUtils ListUtils IntUtils 7 | library FungibleToken 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let two_msgs = 15 | fun (msg1 : Message) => 16 | fun (msg2 : Message) => 17 | let msgs_tmp = one_msg msg2 in 18 | Cons {Message} msg1 msgs_tmp 19 | 20 | (* Error events *) 21 | type Error = 22 | | CodeIsSender 23 | | CodeInsufficientFunds 24 | | CodeInsufficientAllowance 25 | | CodeNotApprovedOperator 26 | 27 | let make_error = 28 | fun (result : Error) => 29 | let result_code = 30 | match result with 31 | | CodeIsSender => Int32 -1 32 | | CodeInsufficientFunds => Int32 -2 33 | | CodeInsufficientAllowance => Int32 -3 34 | | CodeNotApprovedOperator => Int32 -5 35 | end 36 | in 37 | { _exception : "Error"; code : result_code } 38 | 39 | let zero = Uint128 0 40 | 41 | (* Dummy user-defined ADT *) 42 | type Unit = 43 | | Unit 44 | 45 | (* A util function to test equality *) 46 | let f_eq = 47 | fun (a : ByStr20) => 48 | fun (b : ByStr20) => 49 | builtin eq a b 50 | 51 | (* Instantiate a type function to test membership in a list *) 52 | let is_default_operator = @list_mem ByStr20 53 | 54 | let get_val = 55 | fun (some_val: Option Uint128) => 56 | match some_val with 57 | | Some val => val 58 | | None => zero 59 | end 60 | 61 | (***************************************************) 62 | (* The contract definition *) 63 | (***************************************************) 64 | 65 | contract FungibleToken 66 | ( 67 | contract_owner: ByStr20, 68 | name : String, 69 | symbol: String, 70 | decimals: Uint32, 71 | init_supply : Uint128, 72 | default_operators : List ByStr20 73 | ) 74 | 75 | (* Mutable fields *) 76 | 77 | field total_supply : Uint128 = init_supply 78 | 79 | field balances: Map ByStr20 Uint128 80 | = let emp_map = Emp ByStr20 Uint128 in 81 | builtin put emp_map contract_owner init_supply 82 | 83 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 84 | = Emp ByStr20 (Map ByStr20 Uint128) 85 | 86 | field operators: Map ByStr20 (Map ByStr20 Unit) 87 | = Emp ByStr20 (Map ByStr20 Unit) 88 | 89 | field revoked_default_operators : Map ByStr20 (Map ByStr20 Unit) 90 | = Emp ByStr20 (Map ByStr20 Unit) 91 | 92 | (**************************************) 93 | (* Procedures *) 94 | (**************************************) 95 | 96 | procedure ThrowError(err : Error) 97 | e = make_error err; 98 | throw e 99 | end 100 | 101 | procedure IsNotSender(address: ByStr20) 102 | is_sender = builtin eq _sender address; 103 | match is_sender with 104 | | True => 105 | err = CodeIsSender; 106 | ThrowError err 107 | | False => 108 | end 109 | end 110 | 111 | procedure IsApprovedOperator(token_owner: ByStr20, operator: ByStr20) 112 | is_operator_approved <- exists operators[token_owner][operator]; 113 | is_default_operator = is_default_operator f_eq operator default_operators; 114 | is_revoked_operator <- exists revoked_default_operators[token_owner][operator]; 115 | is_default_operator_approved = 116 | let is_not_revoked_operator = negb is_revoked_operator in 117 | andb is_not_revoked_operator is_default_operator; 118 | is_approved = orb is_operator_approved is_default_operator_approved; 119 | match is_approved with 120 | | True => 121 | | False => 122 | err = CodeNotApprovedOperator; 123 | ThrowError err 124 | end 125 | end 126 | 127 | procedure AuthorizedMoveIfSufficientBalance(from: ByStr20, to: ByStr20, amount: Uint128) 128 | o_from_bal <- balances[from]; 129 | bal = get_val o_from_bal; 130 | can_do = uint128_le amount bal; 131 | match can_do with 132 | | True => 133 | (* Subtract amount from from and add it to to address *) 134 | new_from_bal = builtin sub bal amount; 135 | balances[from] := new_from_bal; 136 | (* Adds amount to to address *) 137 | get_to_bal <- balances[to]; 138 | new_to_bal = match get_to_bal with 139 | | Some bal => builtin add bal amount 140 | | None => amount 141 | end; 142 | balances[to] := new_to_bal 143 | | False => 144 | (* Balance not sufficient *) 145 | err = CodeInsufficientFunds; 146 | ThrowError err 147 | end 148 | end 149 | 150 | (***************************************) 151 | (* Transitions *) 152 | (***************************************) 153 | 154 | (* Getter transitions *) 155 | 156 | (* @dev: Check if an address is an operator or default operator of a token_owner. Throw if not. *) 157 | (* @param operator: Address of a potential operator. *) 158 | (* @param token_owner: Address of a token_owner. *) 159 | transition IsOperatorFor(token_owner: ByStr20, operator: ByStr20) 160 | IsApprovedOperator token_owner operator; 161 | msg_to_sender = {_tag : "IsOperatorForCallBack"; _recipient : _sender; _amount : zero; 162 | token_owner: token_owner; operator : operator}; 163 | msgs = one_msg msg_to_sender; 164 | send msgs 165 | end 166 | 167 | (* @dev: Make an address an operator of the caller. *) 168 | (* @param operator: Address to be authorize as operator or *) 169 | (* Re-authorize as default_operator. Cannot be calling address. *) 170 | transition AuthorizeOperator(operator: ByStr20) 171 | IsNotSender operator; 172 | (* Re-authorize default_operator if exists *) 173 | delete revoked_default_operators[_sender][operator]; 174 | (* Authorize new operator if not default_operator *) 175 | verdad = Unit; 176 | operators[_sender][operator] := verdad; 177 | e = {_eventname : "AuthorizeOperatorSuccess"; authorizer : _sender; authorized_operator : operator}; 178 | event e 179 | end 180 | 181 | (* @dev: Revoke an address from being an operator or default_operator of the caller. *) 182 | (* @param operator: Address to be removed as operator or default_operator. *) 183 | transition RevokeOperator(operator: ByStr20) 184 | IsApprovedOperator _sender operator; 185 | (* Remove operator if exists *) 186 | delete operators[_sender][operator]; 187 | (* Revoke default_operator if exists *) 188 | verdad = Unit; 189 | revoked_default_operators[_sender][operator] := verdad; 190 | e = {_eventname : "RevokeOperatorSuccess"; revoker : _sender; revoked_operator : operator}; 191 | event e 192 | end 193 | 194 | (* @dev: Increase the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 195 | (* param spender: Address of the designated approved_spender. *) 196 | (* param amount: Number of tokens to be increased as allowance for the approved_spender. *) 197 | transition IncreaseAllowance(spender: ByStr20, amount: Uint128) 198 | IsNotSender spender; 199 | some_current_allowance <- allowances[_sender][spender]; 200 | current_allowance = get_val some_current_allowance; 201 | new_allowance = builtin add current_allowance amount; 202 | allowances[_sender][spender] := new_allowance; 203 | e = {_eventname : "IncreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 204 | event e 205 | end 206 | 207 | (* @dev: Decrease the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 208 | (* param spender: Address of the designated approved_spender. *) 209 | (* param amount: Number of tokens to be decreased as allowance for the approved_spender. *) 210 | transition DecreaseAllowance(spender: ByStr20, amount: Uint128) 211 | IsNotSender spender; 212 | some_current_allowance <- allowances[_sender][spender]; 213 | current_allowance = get_val some_current_allowance; 214 | new_allowance = 215 | let amount_le_allowance = uint128_le amount current_allowance in 216 | match amount_le_allowance with 217 | | True => builtin sub current_allowance amount 218 | | False => zero 219 | end; 220 | allowances[_sender][spender] := new_allowance; 221 | e = {_eventname : "DecreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 222 | event e 223 | end 224 | 225 | (* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) 226 | (* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) 227 | (* @param to: Address of the recipient whose balance is increased. *) 228 | (* @param amount: Amount of tokens to be sent. *) 229 | transition Transfer(to: ByStr20, amount: Uint128) 230 | AuthorizedMoveIfSufficientBalance _sender to amount; 231 | e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; 232 | event e; 233 | (* Prevent sending to a contract address that does not support transfers of token *) 234 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 235 | sender : _sender; recipient : to; amount : amount}; 236 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 237 | sender : _sender; recipient : to; amount : amount}; 238 | msgs = two_msgs msg_to_recipient msg_to_sender; 239 | send msgs 240 | end 241 | 242 | (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) 243 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 244 | (* @param from: Address of the token_owner whose balance is decreased. *) 245 | (* @param to: Address of the recipient whose balance is increased. *) 246 | (* @param amount: Amount of tokens to be transferred. *) 247 | transition TransferFrom(from: ByStr20, to: ByStr20, amount: Uint128) 248 | o_spender_allowed <- allowances[from][_sender]; 249 | allowed = get_val o_spender_allowed; 250 | can_do = uint128_le amount allowed; 251 | match can_do with 252 | | True => 253 | AuthorizedMoveIfSufficientBalance from to amount; 254 | e = {_eventname : "TransferFromSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 255 | event e; 256 | new_allowed = builtin sub allowed amount; 257 | allowances[from][_sender] := new_allowed; 258 | (* Prevent sending to a contract address that does not support transfers of token *) 259 | msg_to_recipient = {_tag: "RecipientAcceptTransferFrom"; _recipient : to; _amount: zero; 260 | initiator: _sender; sender : from; recipient: to; amount: amount}; 261 | msg_to_sender = {_tag: "TransferFromSuccessCallBack"; _recipient: _sender; _amount: zero; 262 | initiator: _sender; sender: from; recipient: to; amount: amount}; 263 | msgs = two_msgs msg_to_recipient msg_to_sender; 264 | send msgs 265 | | False => 266 | err = CodeInsufficientAllowance; 267 | ThrowError err 268 | end 269 | end 270 | 271 | (* @dev: Moves amount tokens from token_owner to recipient. _sender must be an operator of token_owner. *) 272 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 273 | (* @param from: Address of the token_owner whose balance is decreased. *) 274 | (* @param to: Address of the recipient whose balance is increased. *) 275 | (* @param amount: Amount of tokens to be sent. *) 276 | transition OperatorSend(from: ByStr20, to: ByStr20, amount: Uint128) 277 | IsApprovedOperator from _sender; 278 | AuthorizedMoveIfSufficientBalance from to amount; 279 | e = {_eventname : "OperatorSendSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 280 | event e; 281 | (* Prevent sending to a contract address that does not support transfers of token *) 282 | msg_to_recipient = {_tag : "RecipientAcceptOperatorSend"; _recipient : to; _amount : zero; 283 | initiator : _sender; sender : from; recipient : to; amount : amount}; 284 | msg_to_sender = {_tag : "OperatorSendSuccessCallBack"; _recipient : _sender; _amount : zero; 285 | initiator : _sender; sender : from; recipient : to; amount : amount}; 286 | msgs = two_msgs msg_to_recipient msg_to_sender; 287 | send msgs 288 | end 289 | -------------------------------------------------------------------------------- /reference-contracts/FungibleToken.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | (***************************************************) 4 | (* Associated library *) 5 | (***************************************************) 6 | import IntUtils 7 | library FungibleToken 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let two_msgs = 15 | fun (msg1 : Message) => 16 | fun (msg2 : Message) => 17 | let msgs_tmp = one_msg msg2 in 18 | Cons {Message} msg1 msgs_tmp 19 | 20 | (* Error events *) 21 | type Error = 22 | | CodeIsSender 23 | | CodeInsufficientFunds 24 | | CodeInsufficientAllowance 25 | 26 | let make_error = 27 | fun (result : Error) => 28 | let result_code = 29 | match result with 30 | | CodeIsSender => Int32 -1 31 | | CodeInsufficientFunds => Int32 -2 32 | | CodeInsufficientAllowance => Int32 -3 33 | end 34 | in 35 | { _exception : "Error"; code : result_code } 36 | 37 | let zero = Uint128 0 38 | 39 | (* Dummy user-defined ADT *) 40 | type Unit = 41 | | Unit 42 | 43 | let get_val = 44 | fun (some_val: Option Uint128) => 45 | match some_val with 46 | | Some val => val 47 | | None => zero 48 | end 49 | 50 | (***************************************************) 51 | (* The contract definition *) 52 | (***************************************************) 53 | 54 | contract FungibleToken 55 | ( 56 | contract_owner: ByStr20, 57 | name : String, 58 | symbol: String, 59 | decimals: Uint32, 60 | init_supply : Uint128 61 | ) 62 | 63 | (* Mutable fields *) 64 | 65 | field total_supply : Uint128 = init_supply 66 | 67 | field balances: Map ByStr20 Uint128 68 | = let emp_map = Emp ByStr20 Uint128 in 69 | builtin put emp_map contract_owner init_supply 70 | 71 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 72 | = Emp ByStr20 (Map ByStr20 Uint128) 73 | 74 | (**************************************) 75 | (* Procedures *) 76 | (**************************************) 77 | 78 | procedure ThrowError(err : Error) 79 | e = make_error err; 80 | throw e 81 | end 82 | 83 | procedure IsNotSender(address: ByStr20) 84 | is_sender = builtin eq _sender address; 85 | match is_sender with 86 | | True => 87 | err = CodeIsSender; 88 | ThrowError err 89 | | False => 90 | end 91 | end 92 | 93 | procedure AuthorizedMoveIfSufficientBalance(from: ByStr20, to: ByStr20, amount: Uint128) 94 | o_from_bal <- balances[from]; 95 | bal = get_val o_from_bal; 96 | can_do = uint128_le amount bal; 97 | match can_do with 98 | | True => 99 | (* Subtract amount from from and add it to to address *) 100 | new_from_bal = builtin sub bal amount; 101 | balances[from] := new_from_bal; 102 | (* Adds amount to to address *) 103 | get_to_bal <- balances[to]; 104 | new_to_bal = match get_to_bal with 105 | | Some bal => builtin add bal amount 106 | | None => amount 107 | end; 108 | balances[to] := new_to_bal 109 | | False => 110 | (* Balance not sufficient *) 111 | err = CodeInsufficientFunds; 112 | ThrowError err 113 | end 114 | end 115 | 116 | (***************************************) 117 | (* Transitions *) 118 | (***************************************) 119 | 120 | (* @dev: Increase the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 121 | (* param spender: Address of the designated approved_spender. *) 122 | (* param amount: Number of tokens to be increased as allowance for the approved_spender. *) 123 | transition IncreaseAllowance(spender: ByStr20, amount: Uint128) 124 | IsNotSender spender; 125 | some_current_allowance <- allowances[_sender][spender]; 126 | current_allowance = get_val some_current_allowance; 127 | new_allowance = builtin add current_allowance amount; 128 | allowances[_sender][spender] := new_allowance; 129 | e = {_eventname : "IncreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 130 | event e 131 | end 132 | 133 | (* @dev: Decrease the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 134 | (* param spender: Address of the designated approved_spender. *) 135 | (* param amount: Number of tokens to be decreased as allowance for the approved_spender. *) 136 | transition DecreaseAllowance(spender: ByStr20, amount: Uint128) 137 | IsNotSender spender; 138 | some_current_allowance <- allowances[_sender][spender]; 139 | current_allowance = get_val some_current_allowance; 140 | new_allowance = 141 | let amount_le_allowance = uint128_le amount current_allowance in 142 | match amount_le_allowance with 143 | | True => builtin sub current_allowance amount 144 | | False => zero 145 | end; 146 | allowances[_sender][spender] := new_allowance; 147 | e = {_eventname : "DecreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 148 | event e 149 | end 150 | 151 | (* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) 152 | (* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) 153 | (* @param to: Address of the recipient whose balance is increased. *) 154 | (* @param amount: Amount of tokens to be sent. *) 155 | transition Transfer(to: ByStr20, amount: Uint128) 156 | AuthorizedMoveIfSufficientBalance _sender to amount; 157 | e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; 158 | event e; 159 | (* Prevent sending to a contract address that does not support transfers of token *) 160 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 161 | sender : _sender; recipient : to; amount : amount}; 162 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 163 | sender : _sender; recipient : to; amount : amount}; 164 | msgs = two_msgs msg_to_recipient msg_to_sender; 165 | send msgs 166 | end 167 | 168 | (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) 169 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 170 | (* @param from: Address of the token_owner whose balance is decreased. *) 171 | (* @param to: Address of the recipient whose balance is increased. *) 172 | (* @param amount: Amount of tokens to be transferred. *) 173 | transition TransferFrom(from: ByStr20, to: ByStr20, amount: Uint128) 174 | o_spender_allowed <- allowances[from][_sender]; 175 | allowed = get_val o_spender_allowed; 176 | can_do = uint128_le amount allowed; 177 | match can_do with 178 | | True => 179 | AuthorizedMoveIfSufficientBalance from to amount; 180 | e = {_eventname : "TransferFromSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 181 | event e; 182 | new_allowed = builtin sub allowed amount; 183 | allowances[from][_sender] := new_allowed; 184 | (* Prevent sending to a contract address that does not support transfers of token *) 185 | msg_to_recipient = {_tag: "RecipientAcceptTransferFrom"; _recipient : to; _amount: zero; 186 | initiator: _sender; sender : from; recipient: to; amount: amount}; 187 | msg_to_sender = {_tag: "TransferFromSuccessCallBack"; _recipient: _sender; _amount: zero; 188 | initiator: _sender; sender: from; recipient: to; amount: amount}; 189 | msgs = two_msgs msg_to_recipient msg_to_sender; 190 | send msgs 191 | | False => 192 | err = CodeInsufficientAllowance; 193 | ThrowError err 194 | end 195 | end 196 | -------------------------------------------------------------------------------- /reference-contracts/MetaFungibleToken.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | (***************************************************) 4 | (* Associated library *) 5 | (***************************************************) 6 | import IntUtils 7 | library MetaFungibleToken 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let two_msgs = 15 | fun (msg1 : Message) => 16 | fun (msg2 : Message) => 17 | let msgs_tmp = one_msg msg2 in 18 | Cons {Message} msg1 msgs_tmp 19 | 20 | (* Error events *) 21 | type Error = 22 | | CodeIsSender 23 | | CodeInsufficientFunds 24 | | CodeInsufficientAllowance 25 | | CodeChequeVoid 26 | | CodeSignatureInvalid 27 | | CodeInvalidSigner 28 | 29 | let make_error = 30 | fun (result : Error) => 31 | let result_code = 32 | match result with 33 | | CodeIsSender => Int32 -1 34 | | CodeInsufficientFunds => Int32 -2 35 | | CodeInsufficientAllowance => Int32 -3 36 | | CodeChequeVoid => Int32 -4 37 | | CodeSignatureInvalid => Int32 -5 38 | | CodeInvalidSigner => Int32 -6 39 | end 40 | in 41 | { _exception : "Error"; code : result_code } 42 | 43 | let zero = Uint128 0 44 | let prefix = "0x" 45 | (* Dummy user-defined ADT *) 46 | type Unit = 47 | | Unit 48 | 49 | let get_val = 50 | fun (some_val: Option Uint128) => 51 | match some_val with 52 | | Some val => val 53 | | None => zero 54 | end 55 | 56 | (***************************************************) 57 | (* The contract definition *) 58 | (***************************************************) 59 | 60 | contract MetaFungibleToken 61 | ( 62 | contract_owner: ByStr20, 63 | name : String, 64 | symbol: String, 65 | decimals: Uint32, 66 | init_supply : Uint128 67 | ) 68 | 69 | (* Mutable fields *) 70 | 71 | field total_supply : Uint128 = init_supply 72 | 73 | field balances: Map ByStr20 Uint128 74 | = let emp_map = Emp ByStr20 Uint128 in 75 | builtin put emp_map contract_owner init_supply 76 | 77 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 78 | = Emp ByStr20 (Map ByStr20 Uint128) 79 | 80 | field void_cheques: Map ByStr ByStr20 81 | = Emp ByStr ByStr20 82 | (**************************************) 83 | (* Procedures *) 84 | (**************************************) 85 | 86 | procedure ThrowError(err : Error) 87 | e = make_error err; 88 | throw e 89 | end 90 | 91 | procedure IsNotSender(address: ByStr20) 92 | is_sender = builtin eq _sender address; 93 | match is_sender with 94 | | True => 95 | err = CodeIsSender; 96 | ThrowError err 97 | | False => 98 | end 99 | end 100 | 101 | procedure AuthorizedMoveIfSufficientBalance(from: ByStr20, to: ByStr20, amount: Uint128) 102 | o_from_bal <- balances[from]; 103 | bal = get_val o_from_bal; 104 | can_do = uint128_le amount bal; 105 | match can_do with 106 | | True => 107 | (* Subtract amount from from and add it to to address *) 108 | new_from_bal = builtin sub bal amount; 109 | balances[from] := new_from_bal; 110 | (* Adds amount to to address *) 111 | get_to_bal <- balances[to]; 112 | new_to_bal = match get_to_bal with 113 | | Some bal => builtin add bal amount 114 | | None => amount 115 | end; 116 | balances[to] := new_to_bal 117 | | False => 118 | (* Balance not sufficient *) 119 | err = CodeInsufficientFunds; 120 | ThrowError err 121 | end 122 | end 123 | 124 | (***************************************) 125 | (* Transitions *) 126 | (***************************************) 127 | 128 | (* @dev: Increase the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 129 | (* param spender: Address of the designated approved_spender. *) 130 | (* param amount: Number of tokens to be increased as allowance for the approved_spender. *) 131 | transition IncreaseAllowance(spender: ByStr20, amount: Uint128) 132 | IsNotSender spender; 133 | some_current_allowance <- allowances[_sender][spender]; 134 | current_allowance = get_val some_current_allowance; 135 | new_allowance = builtin add current_allowance amount; 136 | allowances[_sender][spender] := new_allowance; 137 | e = {_eventname : "IncreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 138 | event e 139 | end 140 | 141 | (* @dev: Decrease the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 142 | (* param spender: Address of the designated approved_spender. *) 143 | (* param amount: Number of tokens to be decreased as allowance for the approved_spender. *) 144 | transition DecreaseAllowance(spender: ByStr20, amount: Uint128) 145 | IsNotSender spender; 146 | some_current_allowance <- allowances[_sender][spender]; 147 | current_allowance = get_val some_current_allowance; 148 | new_allowance = 149 | let amount_le_allowance = uint128_le amount current_allowance in 150 | match amount_le_allowance with 151 | | True => builtin sub current_allowance amount 152 | | False => zero 153 | end; 154 | allowances[_sender][spender] := new_allowance; 155 | e = {_eventname : "DecreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 156 | event e 157 | end 158 | 159 | (* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) 160 | (* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) 161 | (* @param to: Address of the recipient whose balance is increased. *) 162 | (* @param amount: Amount of tokens to be sent. *) 163 | transition Transfer(to: ByStr20, amount: Uint128) 164 | AuthorizedMoveIfSufficientBalance _sender to amount; 165 | e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; 166 | event e; 167 | (* Prevent sending to a contract address that does not support transfers of token *) 168 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 169 | sender : _sender; recipient : to; amount : amount}; 170 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 171 | sender : _sender; recipient : to; amount : amount}; 172 | msgs = two_msgs msg_to_recipient msg_to_sender; 173 | send msgs 174 | end 175 | 176 | (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) 177 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 178 | (* @param from: Address of the token_owner whose balance is decreased. *) 179 | (* @param to: Address of the recipient whose balance is increased. *) 180 | (* @param amount: Amount of tokens to be transferred. *) 181 | transition TransferFrom(from: ByStr20, to: ByStr20, amount: Uint128) 182 | o_spender_allowed <- allowances[from][_sender]; 183 | allowed = get_val o_spender_allowed; 184 | can_do = uint128_le amount allowed; 185 | match can_do with 186 | | True => 187 | AuthorizedMoveIfSufficientBalance from to amount; 188 | e = {_eventname : "TransferFromSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 189 | event e; 190 | new_allowed = builtin sub allowed amount; 191 | allowances[from][_sender] := new_allowed; 192 | (* Prevent sending to a contract address that does not support transfers of token *) 193 | msg_to_recipient = {_tag: "RecipientAcceptTransferFrom"; _recipient : to; _amount: zero; 194 | initiator: _sender; sender : from; recipient: to; amount: amount}; 195 | msg_to_sender = {_tag: "TransferFromSuccessCallBack"; _recipient: _sender; _amount: zero; 196 | initiator: _sender; sender: from; recipient: to; amount: amount}; 197 | msgs = two_msgs msg_to_recipient msg_to_sender; 198 | send msgs 199 | | False => 200 | err = CodeInsufficientAllowance; 201 | ThrowError err 202 | end 203 | end 204 | 205 | (* @dev: Moves amount tokens from token_owner to recipient. *) 206 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 207 | (* @param pubkey: Public Key of the token_owner whose balance is decreased. *) 208 | (* @param to: Address of the recipient whose balance is increased. *) 209 | (* @param amount: Amount of tokens to be sent. *) 210 | (* @param fee: Reward taken from the cheque senders balance for the relayer. *) 211 | (* @param nonce: A random value included in the cheque to make each unique. *) 212 | (* @param signature: The signature of the cheque by the token owner to authorize spend. *) 213 | transition ChequeSend(pubkey: ByStr33, to: ByStr20, amount: Uint128, fee: Uint128, nonce:Uint128, signature: ByStr64) 214 | from = builtin schnorr_get_address pubkey; 215 | to_hash = builtin sha256hash to; 216 | amount_hash = builtin sha256hash amount; 217 | contract_hash = builtin sha256hash _this_address; 218 | fee_hash = builtin sha256hash fee; 219 | nonce_hash = builtin sha256hash nonce; 220 | p0_hash = builtin concat to_hash amount_hash; 221 | p1_hash = builtin concat p0_hash contract_hash; 222 | p2_hash = builtin concat p1_hash fee_hash; 223 | p3_hash = builtin concat p2_hash nonce_hash; 224 | cheque_hash = builtin to_bystr p3_hash; 225 | ev = {_eventname : "CalculatedHash"; from:from ; to_hash:to_hash ; amount_hash:amount_hash; contract_hash:contract_hash;fee_hash:fee_hash ; nonce_hash:nonce_hash ; chequehash:cheque_hash }; 226 | event ev; 227 | cheque_invalid <- exists void_cheques[cheque_hash]; 228 | match cheque_invalid with 229 | | False => 230 | valid_sig = builtin schnorr_verify pubkey cheque_hash signature; 231 | match valid_sig with 232 | | True => 233 | AuthorizedMoveIfSufficientBalance from to amount; 234 | AuthorizedMoveIfSufficientBalance from _sender fee; 235 | void_cheques[cheque_hash] :=_sender; 236 | e = {_eventname : "ChequeSendSuccess"; initiator : _sender; sender: from; recipient : to; amount : amount}; 237 | event e; 238 | (* Prevent sending to a contract address that does not support transfers of token *) 239 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 240 | sender : _sender; recipient : to; amount : amount}; 241 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 242 | sender : _sender; recipient : to; amount : amount}; 243 | msgs = two_msgs msg_to_recipient msg_to_sender; 244 | send msgs 245 | | False => 246 | err = CodeSignatureInvalid; 247 | ThrowError err 248 | end 249 | | True => 250 | err = CodeChequeVoid; 251 | ThrowError err 252 | end 253 | end 254 | 255 | 256 | (* @dev: Voids a cheque that _sender does not wish to be processed *) 257 | (* @dev: Balance of recipient will remain the same. *) 258 | (* @param pubkey: Public Key of the token_owner within the cheque. *) 259 | (* @param to: Address of the recipient within the cheque. *) 260 | (* @param amount: Amount of tokens which would have been sent within the cheque. *) 261 | (* @param fee: Reward to be taken from the cheque senders balance if the cheque was processed. *) 262 | (* @param nonce: A random value included in the cheque to make each unique. *) 263 | (* @param signature: The signature of the cheque by the token owner that authorized the spend. *) 264 | transition ChequeVoid(pubkey: ByStr33, to: ByStr20, amount: Uint128, fee: Uint128, nonce:Uint128, signature: ByStr64) 265 | from = builtin schnorr_get_address pubkey; 266 | to_hash = builtin sha256hash to; 267 | amount_hash = builtin sha256hash amount; 268 | contract_hash = builtin sha256hash _this_address; 269 | fee_hash = builtin sha256hash fee; 270 | nonce_hash = builtin sha256hash nonce; 271 | p0_hash = builtin concat to_hash amount_hash; 272 | p1_hash = builtin concat p0_hash contract_hash; 273 | p2_hash = builtin concat p1_hash fee_hash; 274 | p3_hash = builtin concat p2_hash nonce_hash; 275 | cheque_hash = builtin to_bystr p3_hash; 276 | cheque_invalid <- exists void_cheques[cheque_hash]; 277 | match cheque_invalid with 278 | | False => 279 | valid_sig = builtin schnorr_verify pubkey cheque_hash signature; 280 | match valid_sig with 281 | | True => 282 | void_cheques[cheque_hash] :=_sender; 283 | e = {_eventname : "ChequeVoidSuccess"; initiator : _sender; sender: from; recipient : to; amount : amount}; 284 | event e 285 | | False => 286 | err = CodeSignatureInvalid; 287 | ThrowError err 288 | end 289 | | True => 290 | err = CodeChequeVoid; 291 | ThrowError err 292 | end 293 | end -------------------------------------------------------------------------------- /reference-contracts/multisig_wallet.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | import ListUtils BoolUtils 4 | 5 | (***************************************************) 6 | (* Associated library *) 7 | (***************************************************) 8 | library WalletLib 9 | 10 | (* Event for communicating a new transaction id *) 11 | let mk_transaction_added_event = 12 | fun (tc : Uint32) => 13 | fun (recipient : ByStr20) => 14 | fun (amount : Uint128) => 15 | fun (tag : String) => 16 | { _eventname : "Transaction created" ; transactionId : tc; recipient : recipient; amount : amount; tag : tag } 17 | 18 | (* Event for communicating the execution of a transaction *) 19 | let mk_transaction_executed_event = 20 | fun (tc : Uint32) => 21 | fun (recipient : ByStr20) => 22 | fun (amount : Uint128) => 23 | fun (tag : String) => 24 | { _eventname : "Transaction executed"; transactionId : tc; recipient : recipient; amount : amount; tag : tag } 25 | 26 | (* Event for communicating that a transaction was signed *) 27 | let mk_signed_transaction_event = 28 | fun (tc : Uint32) => 29 | { _eventname : "Transaction signed"; transactionId : tc } 30 | 31 | (* Event for communicating that a signature was revoked *) 32 | let mk_signature_revoked_event = 33 | fun (tc : Uint32) => 34 | { _eventname : "Signature revoked"; transactionId : tc } 35 | 36 | type Error = 37 | | NonOwnerCannotSign 38 | | UnknownTransactionId 39 | | InsufficientFunds 40 | | NoSignatureListFound 41 | | AlreadySigned 42 | | NotAlreadySigned 43 | | InvalidContract 44 | | InvalidAmount 45 | | NotEnoughSignatures 46 | | SenderMayNotExecute 47 | | NonOwnerCannotSubmit 48 | | IncorrectSignatureCount 49 | 50 | (* Error events *) 51 | let mk_error_event = 52 | fun (err : Error) => 53 | let err_code = 54 | match err with 55 | | NonOwnerCannotSign => Int32 -1 56 | | UnknownTransactionId => Int32 -2 57 | | InsufficientFunds => Int32 -3 58 | | NoSignatureListFound => Int32 -4 59 | | AlreadySigned => Int32 -5 60 | | NotAlreadySigned => Int32 -6 61 | | InvalidContract => Int32 -7 62 | | InvalidAmount => Int32 -8 63 | | NotEnoughSignatures => Int32 -9 64 | | SenderMayNotExecute => Int32 -10 65 | | NonOwnerCannotSubmit => Int32 -11 66 | | IncorrectSignatureCount => Int32 -12 67 | end in 68 | { _eventname : "WalletError" ; err_code : err_code } 69 | 70 | let t = True 71 | let f = False 72 | let zero = Uint32 0 73 | let one = Uint32 1 74 | let transaction_inc = one 75 | 76 | (* One (potential) transaction, consisting of a recipient address, an amount, *) 77 | (* and a tag (in case the recipient is another contract *) 78 | type Transaction = 79 | | Trans of ByStr20 Uint128 String 80 | 81 | (* Make map of owners *) 82 | let mk_owners_map = 83 | fun (owners : List ByStr20) => 84 | let init = Emp ByStr20 Bool in 85 | let iter = 86 | fun (acc : Map ByStr20 Bool) => 87 | fun (cur_owner : ByStr20) => 88 | (* Add owner unconditionally. We check for duplicates later *) 89 | builtin put acc cur_owner t 90 | in 91 | let folder = @list_foldl ByStr20 (Map ByStr20 Bool) in 92 | folder iter init owners 93 | 94 | (* Create one transaction message *) 95 | let transaction_msg = 96 | fun (recipient : ByStr20) => 97 | fun (amount : Uint128) => 98 | fun (tag : String) => 99 | {_tag : tag; _recipient : recipient; _amount : amount } 100 | 101 | (* Wrap one transaction message as singleton list *) 102 | let transaction_msg_as_list = 103 | fun (recipient : ByStr20) => 104 | fun (amount : Uint128) => 105 | fun (tag : String) => 106 | let one_msg = 107 | fun (msg : Message) => 108 | let nil_msg = Nil {Message} in 109 | Cons {Message} msg nil_msg in 110 | let msg = transaction_msg recipient amount tag in 111 | one_msg msg 112 | 113 | (***************************************************) 114 | (* The contract definition *) 115 | (* *) 116 | (* This contract holds funds that can be paid out *) 117 | (* to arbitrary users, provided that enough people *) 118 | (* in the collection of owners sign off on the *) 119 | (* payout. *) 120 | (* *) 121 | (* The transaction must be added to the contract *) 122 | (* before signatures can be collected. Once enough *) 123 | (* signatures are collected, the recipient can ask *) 124 | (* for the transaction to be executed and the *) 125 | (* money paid out. *) 126 | (* *) 127 | (* If an owner changes his mind about a *) 128 | (* transaction, the signature can be revoked until *) 129 | (* the transaction is executed. *) 130 | (* *) 131 | (* This wallet does not allow adding or removing *) 132 | (* owners, or changing the number of required *) 133 | (* signatures. To do any of those things, perform *) 134 | (* the following steps: *) 135 | (* *) 136 | (* 1. Deploy a new wallet with owners and *) 137 | (* required_signatures set to the new values. *) 138 | (* MAKE SURE THAT THE NEW WALLET HAS BEEN *) 139 | (* SUCCESFULLY DEPLOYED WITH THE CORRECT *) 140 | (* PARAMETERS BEFORE CONTINUING! *) 141 | (* 2. Invoke the SubmitTransaction transition on *) 142 | (* the old wallet with the following *) 143 | (* parameters: *) 144 | (* recipient : The address of the new wallet *) 145 | (* amount : The _balance of the old wallet *) 146 | (* tag : "AddFunds" *) 147 | (* 3. Have (a sufficient number of) the owners of *) 148 | (* the old contract invoke the SignTransaction *) 149 | (* transition on the old wallet. The parameter *) 150 | (* transactionId should be set to the Id of the *) 151 | (* transaction created in step 2. *) 152 | (* 4. Have one of the owners of the old contract *) 153 | (* invoke the ExecuteTransaction transition on *) 154 | (* the old contract. This will cause the entire *) 155 | (* balance of the old contract to be *) 156 | (* transferred to the new wallet. Note that no *) 157 | (* un-executed transactions will be transferred *) 158 | (* to the new wallet along with the funds. *) 159 | (* *) 160 | (* WARNING: If a sufficient number of owners lose *) 161 | (* their private keys, or for any other reason are *) 162 | (* unable or unwilling to sign for new *) 163 | (* transactions, the funds in the wallet will be *) 164 | (* locked forever. It is therefore a good idea to *) 165 | (* set required_signatures to a value strictly *) 166 | (* less than the number of owners, so that the *) 167 | (* remaining owners can retrieve the funds should *) 168 | (* such a scenario occur. *) 169 | (* *) 170 | (* If an owner loses his private key, the *) 171 | (* remaining owners should move the funds to a new *) 172 | (* wallet (using the workflow described above) to *) 173 | (* ensure that funds are not locked if another *) 174 | (* owner loses his private key. The owner who *) 175 | (* originally lost his private key can generate a *) 176 | (* new key, and the corresponding address be added *) 177 | (* to the new wallet, so that the same set of *) 178 | (* persons own the new wallet. *) 179 | (* *) 180 | (***************************************************) 181 | contract Wallet 182 | ( 183 | owners_list : List ByStr20, 184 | required_signatures : Uint32 185 | ) 186 | with 187 | let len = @list_length ByStr20 in 188 | let no_of_owners = len owners_list in 189 | let owners_ok = builtin lt zero no_of_owners in 190 | let required_sigs_not_too_low = builtin lt zero required_signatures in 191 | let required_sigs_too_high = builtin lt no_of_owners required_signatures in 192 | let required_sigs_not_too_high = negb required_sigs_too_high in 193 | let required_sigs_ok = andb required_sigs_not_too_high required_sigs_not_too_low in 194 | let all_ok = andb required_sigs_ok owners_ok in 195 | (* Building the owners map is expensive, so avoid checking the owners map until *) 196 | (* everything else has been checked *) 197 | match all_ok with 198 | | True => 199 | let owners_map = mk_owners_map owners_list in 200 | let size_of_owners_map = builtin size owners_map in 201 | builtin eq size_of_owners_map no_of_owners 202 | | False => 203 | False 204 | end 205 | => 206 | 207 | (* adr -> True indicates an owner *) 208 | (* adr not in map indicates non-owner *) 209 | (* adr -> False is not used *) 210 | field owners : Map ByStr20 Bool = mk_owners_map owners_list 211 | 212 | field transactionCount : Uint32 = Uint32 0 213 | 214 | (* Collected signatures for transactions *) 215 | field signatures : Map Uint32 (Map ByStr20 Bool) = 216 | Emp Uint32 (Map ByStr20 Bool) 217 | 218 | (* Running count of collected signatures for transactions *) 219 | field signature_counts : Map Uint32 Uint32 = 220 | Emp Uint32 Uint32 221 | 222 | (* Transactions *) 223 | field transactions : Map Uint32 Transaction = 224 | Emp Uint32 Transaction 225 | 226 | procedure MakeError (err : Error) 227 | e = mk_error_event err; 228 | event e 229 | end 230 | 231 | (* Add signature to signature list *) 232 | procedure AddSignature (transactionId : Uint32, signee : ByStr20) 233 | sig <- exists signatures[transactionId][signee]; 234 | match sig with 235 | | False => 236 | count <- signature_counts[transactionId]; 237 | match count with 238 | | None => 239 | (* 0 signatures *) 240 | signature_counts[transactionId] := one 241 | | Some c => 242 | new_c = builtin add c one; 243 | signature_counts[transactionId] := new_c 244 | end; 245 | signatures[transactionId][signee] := t; 246 | e = mk_signed_transaction_event transactionId; 247 | event e 248 | | True => 249 | (* Already signed *) 250 | err = AlreadySigned; 251 | MakeError err 252 | end 253 | end 254 | 255 | (* Submit a transaction for future signoff *) 256 | transition SubmitTransaction (recipient : ByStr20, amount : Uint128, tag : String) 257 | (* Only allow owners to submit new transactions *) 258 | sender_is_owner <- exists owners[_sender]; 259 | match sender_is_owner with 260 | | False => 261 | err = NonOwnerCannotSubmit; 262 | MakeError err 263 | | True => 264 | tc <- transactionCount; 265 | zero = Uint128 0; 266 | amount_is_zero = builtin eq amount zero; 267 | match amount_is_zero with 268 | | True => 269 | (* Illegal transaction *) 270 | err = InvalidAmount; 271 | MakeError err 272 | | False => 273 | (* Create new transaction *) 274 | transaction = Trans recipient amount tag; 275 | (* Add transaction to outstanding list of transactions *) 276 | transactions[tc] := transaction; 277 | (* Sender implicitly signs *) 278 | AddSignature tc _sender; 279 | (* Increment transaction counter *) 280 | tc_new = builtin add tc transaction_inc; 281 | (* Update transaction count *) 282 | transactionCount := tc_new; 283 | (* Create event with transaction Id *) 284 | e = mk_transaction_added_event tc recipient amount tag; 285 | event e 286 | end 287 | end 288 | end 289 | 290 | (* Sign off on an existing transaction *) 291 | transition SignTransaction (transactionId : Uint32) 292 | (* Only the owner is allowed to sign off transactions *) 293 | sender_is_owner <- exists owners[_sender]; 294 | match sender_is_owner with 295 | | False => 296 | err = NonOwnerCannotSign; 297 | MakeError err 298 | | True => 299 | (* Transaction must have been submitted *) 300 | transaction <- transactions[transactionId]; 301 | match transaction with 302 | | None => 303 | err = UnknownTransactionId; 304 | MakeError err 305 | | Some _ => 306 | (* Remaining error cases handled by AddSignature *) 307 | AddSignature transactionId _sender 308 | end 309 | end 310 | end 311 | 312 | (* Delete transaction and signatures *) 313 | procedure DeleteTransaction (transactionId : Uint32) 314 | delete transactions[transactionId]; 315 | delete signatures[transactionId]; 316 | delete signature_counts[transactionId] 317 | end 318 | 319 | (* Execute signed-off transaction *) 320 | transition ExecuteTransaction (transactionId : Uint32) 321 | transaction_opt <- transactions[transactionId]; 322 | match transaction_opt with 323 | | None => 324 | (* Transaction was not found. *) 325 | err = UnknownTransactionId; 326 | MakeError err 327 | | Some (Trans recipient amount tag) => 328 | (* Only the recipient or an owner can execute the transaction *) 329 | recipient_is_sender = builtin eq recipient _sender; 330 | sender_is_owner <- exists owners[_sender]; 331 | sender_may_execute = orb recipient_is_sender sender_is_owner; 332 | match sender_may_execute with 333 | | False => 334 | err = SenderMayNotExecute; 335 | MakeError err 336 | | True => 337 | (* Check for sufficient funds *) 338 | bal <- _balance; 339 | not_enough_money = builtin lt bal amount; 340 | match not_enough_money with 341 | | True => 342 | err = InsufficientFunds; 343 | MakeError err 344 | | False => 345 | sig_count_opt <- signature_counts[transactionId]; 346 | match sig_count_opt with 347 | | None => 348 | (* Signature count not found, even though the transaction exists.*) 349 | err = NoSignatureListFound; 350 | MakeError err 351 | | Some sig_count => 352 | not_enough_signatures = builtin lt sig_count required_signatures; 353 | match not_enough_signatures with 354 | | True => 355 | err = NotEnoughSignatures; 356 | MakeError err 357 | | False => 358 | (* Transaction approved, and enough money available. *) 359 | (* Remove transaction and signatures, and execute. *) 360 | DeleteTransaction transactionId; 361 | msgs = transaction_msg_as_list recipient amount tag; 362 | send msgs; 363 | e = mk_transaction_executed_event transactionId recipient amount tag; 364 | event e 365 | end 366 | end 367 | end 368 | end 369 | end 370 | end 371 | 372 | (* Revoke signature of existing transaction, if it has not yet been executed. *) 373 | transition RevokeSignature (transactionId : Uint32) 374 | sig <- exists signatures[transactionId][_sender]; 375 | match sig with 376 | | False => 377 | err = NotAlreadySigned; 378 | MakeError err 379 | | True => 380 | count <- signature_counts[transactionId]; 381 | match count with 382 | | None => 383 | err = IncorrectSignatureCount; 384 | MakeError err 385 | | Some c => 386 | c_is_zero = builtin eq c zero; 387 | match c_is_zero with 388 | | True => 389 | err = IncorrectSignatureCount; 390 | MakeError err 391 | | False => 392 | new_c = builtin sub c one; 393 | signature_counts[transactionId] := new_c; 394 | delete signatures[transactionId][_sender]; 395 | e = mk_signature_revoked_event transactionId; 396 | event e 397 | end 398 | end 399 | end 400 | end 401 | 402 | (* Add funds to wallet *) 403 | transition AddFunds () 404 | accept 405 | end 406 | -------------------------------------------------------------------------------- /reference-contracts/style-guide.md: -------------------------------------------------------------------------------- 1 | # Style Guide 2 | 3 | # Introduction 4 | 5 | The purpose of this guide is to provide coding conventions for writing scilla code. 6 | 7 | Please note that as Scilla is an evolving language, this style guide will change over time to reflect the latest usability feedback and features. This guide shall hence be thought of as an evolving document over time, instead of a commandment that is set in stone. 8 | 9 | ## General 10 | 11 | Scilla belongs to the meta-language ("ML") family of languages, and therefore, the style shall be consistent with the principles laid out in ML-languagues. 12 | 13 | Scilla is developed off the back of OCaml, therfore our guidelines are very similar to the ones laid out by the OCaml community. There are however, some variations that we have made intentionally to cater for the design and purpose of Scilla. For example, OCaml does not specify naming conventions for variables, but we hope that Scilla developers will follow the `snake_case` convention. This is because smart contract parameters are meant to be interoperable, and sticking to consistent cases can make it easier for people to read your code and send transactions to your contract if required. 14 | 15 | In the event where you encounter something that is _not_ covered by this document, you can refer to [https://ocaml.org/learn/tutorials/guidelines.html](https://ocaml.org/learn/tutorials/guidelines.html) 16 | 17 | # Code Layout 18 | 19 | ### **Indentation** 20 | 21 | Use 2 spaces per indentation level. 22 | 23 | ### **Tabs or Spaces** 24 | 25 | Spaces are the preferred indentation method. 26 | 27 | ### Blank lines 28 | 29 | Surround the top of every procedure, library and transition with blank spaces 30 | 31 | ### Maximum Line Length 32 | 33 | As recommended by [PEP 8](<[https://www.python.org/dev/peps/pep-0008/#maximum-line-length](https://www.python.org/dev/peps/pep-0008/#maximum-line-length)>), we recommend that you keep lines in Scilla contracts to a maximum of 80 characters for readability. 34 | 35 | Wrapped lines should conform to the following rules: 36 | 37 | - The first argument should not be attached to the opening parenthesis 38 | - One, and only one, indent should be used 39 | - Each argument should fall on its own line 40 | - The terminating element, );, should be placed on the final line by itself 41 | 42 | ```ocaml 43 | (* Good *) 44 | transition MakeOrder( 45 | token_a: ByStr20, 46 | value_a: Uint128, 47 | token_b: ByStr20, 48 | value_b: Uint128, 49 | expiration_block: BNum 50 | ) 51 | 52 | (* Bad *) 53 | (* Reason: All the parameters are in the same line, which makes it hard to read *) 54 | transition MakeOrder(token_a: ByStr20, value_a: Uint128, token_b: ByStr20, value_b: Uint128, expiration_block: BNum) 55 | ``` 56 | 57 | ## Indentation Rules 58 | 59 | - Indentation for `let...in` expressions 60 | - The expression following a definition introduced by `let` is indented to the same level as the keyword `let`, and the keyword `in` which introduces it is written at the end of the line. 61 | 62 | ```ocaml 63 | 64 | (* Code snippet from Shogi.scilla *) 65 | 66 | (* Good *) 67 | ... 68 | let nil_path = Nil {Square} in 69 | let first_square = move_one_square square north in 70 | let final_square = move_one_square first_square direction in 71 | Cons {Square} final_square nil_path 72 | 73 | (* Bad *) 74 | ... 75 | let nil_path = Nil {Square} in 76 | let first_square = move_one_square square north in 77 | let final_square = move_one_square first_square direction in 78 | Cons {Square} final_square nil_path 79 | ``` 80 | 81 | ## Naming Conventions 82 | 83 | Definition of the various styles: 84 | 85 | - `PascalCase` 86 | - `camelCase` 87 | - `snake_case` 88 | - `lowercase` 89 | - `UPPER_CASE` 90 | - `Camel_snake` 91 | 92 | > **NOTE:** Naming variables with a leading underscore is not allowed in Scilla (e.g. `_to`) 93 | 94 | ### Contract and Library Names 95 | 96 | - Library and Contracts should be named in PascalCase. E.g. `FungibleToken`, `NonFungibleToken` 97 | - Contract and Library name should match the filename 98 | 99 | ```ocaml 100 | 101 | (* Good *) 102 | (* NonFungibleToken.scilla *) 103 | library NonFungibleToken 104 | 105 | contract NonFungibleToken 106 | ( 107 | param1: type, 108 | ... 109 | ) 110 | 111 | (* Bad *) 112 | (* nonfungibletoken-finalfinalfinal.scilla *) 113 | library NonFungibleToken 114 | 115 | contract NonFungibleToken 116 | ( 117 | param1: type, 118 | ... 119 | ) 120 | ``` 121 | 122 | ### Event Names 123 | 124 | - Event names should be named in PascalCase. E.g. `TransferFromSuccess` 125 | - Please note that `scilla-checker` checks for the parameters as well. Overloading an event name is not allowed 126 | - Event name should be concise and contained within a single word 127 | 128 | ```ocaml 129 | (* Good *) 130 | e = {_eventname: "TransferFromSuccess"; status: "Success" sender: _sender; ...}; 131 | e = {_eventname: "TransferFromFailure"; status: "Error"; message: "Unauthorised; ...}; 132 | 133 | (* Bad *) 134 | e = {_eventname: "TransferFrom Not Successful"} 135 | e = {_eventname: "TransferFrom: Success"} 136 | ``` 137 | 138 | ### Transition Name 139 | 140 | - TransitionName should be named in PascalCase 141 | - E.g. `TransferFrom` 142 | 143 | ### Abstract Data Types (ADT) 144 | 145 | - ADT names should be in PascalCase 146 | 147 | > **NOTE:** The checker currently checks if the error is capitalised, but it does not check for PascalCase 148 | 149 | Example: 150 | 151 | ```ocaml 152 | (* Good *) 153 | type Error = 154 | | GameOver 155 | | PlayingOutOfTurn 156 | | IllegalAction 157 | | InternalError 158 | 159 | (* Bad *) 160 | type Error = 161 | | Gameover 162 | | Playingoutofturn 163 | | Illegalsction 164 | | Internalerror 165 | 166 | (* Bad (Syntax Error as error types are not capitalised) *) 167 | type Error = 168 | | gameover 169 | | playingoutofturn 170 | | illegalsction 171 | | internalerror 172 | 173 | 174 | ``` 175 | 176 | ### Local Variable Names 177 | 178 | - To be consistent with OCaml styling guidelines, variable names should be named in snake_case. Variable names with only one word should be in lowercase E.g. `one_msg`,`transfer_amt`, `zero`, `player1` 179 | 180 | ```ocaml 181 | (* Good *) 182 | let code_success = Uint32 0 183 | 184 | (* Bad *) 185 | let codeSuccess = Uint32 0 186 | let CodeSuccess = Uint32 0 187 | let Code_success = Uint32 0 188 | ``` 189 | -------------------------------------------------------------------------------- /tests/globalConfig.ts: -------------------------------------------------------------------------------- 1 | import { BN, Zilliqa, bytes, units } from "@zilliqa-js/zilliqa"; 2 | import Long from "long"; 3 | 4 | export const API = `http://localhost:${process.env["PORT"]}`; // Zilliqa Isolated Server 5 | export const CHAIN_ID = 222; 6 | export const MSG_VERSION = 1; 7 | export const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); 8 | 9 | export const JEST_WORKER_ID = Number(process.env["JEST_WORKER_ID"]); 10 | export const GENESIS_PRIVATE_KEY = global.GENESIS_PRIVATE_KEYS[JEST_WORKER_ID - 1]; 11 | 12 | export const zilliqa = new Zilliqa(API); 13 | zilliqa.wallet.addByPrivateKey(GENESIS_PRIVATE_KEY); 14 | 15 | export const GAS_PRICE = units.toQa("2000", units.Units.Li); 16 | 17 | export const FAUCET_PARAMS = { 18 | version: VERSION, 19 | amount: new BN(units.toQa("100000000", units.Units.Zil)), 20 | gasPrice: GAS_PRICE, 21 | gasLimit: Long.fromNumber(50), 22 | }; 23 | -------------------------------------------------------------------------------- /tests/testutils.ts: -------------------------------------------------------------------------------- 1 | import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; 2 | import { getAddressFromPrivateKey, schnorr } from "@zilliqa-js/zilliqa"; 3 | import { FAUCET_PARAMS, zilliqa } from "./globalConfig"; 4 | 5 | export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; 6 | 7 | export async function getAccounts(numberOfAccounts: number) { 8 | const accounts = Array.from({ length: numberOfAccounts }, schnorr.generatePrivateKey).map( 9 | (privateKey) => ({ 10 | privateKey, 11 | address: getAddressFromPrivateKey(privateKey), 12 | }) 13 | ); 14 | 15 | for (const { privateKey, address } of accounts) { 16 | zilliqa.wallet.addByPrivateKey(privateKey); 17 | const tx = await zilliqa.blockchain.createTransaction( 18 | zilliqa.transactions.new( 19 | { 20 | ...FAUCET_PARAMS, 21 | toAddr: address, 22 | }, 23 | false 24 | ) 25 | ); 26 | if (!tx.getReceipt()?.success) { 27 | throw new Error(); 28 | } 29 | } 30 | 31 | return accounts; 32 | } 33 | 34 | export type ContractTestCaseDefinition = { 35 | name: string, 36 | transition: string, 37 | getSender: () => string, 38 | getParams: () => Record>, 39 | error: number | undefined, 40 | want: { 41 | expectState: (state: any) => void, 42 | events: Array<{ 43 | name: string, 44 | getParams: () => Record>, 45 | }>, 46 | transitions: Array<{ 47 | tag: string, 48 | getParams: () => Record>, 49 | }>, 50 | } | undefined, 51 | } 52 | 53 | export const expectEvents = (events, want) => { 54 | if (events === undefined) { 55 | expect(undefined).toBe(want); 56 | } 57 | 58 | for (const [index, event] of events.entries()) { 59 | expect(event._eventname).toBe(want[index].name); 60 | const wantParams = scillaJSONParams(want[index].getParams()); 61 | expect(JSON.stringify(event.params)).toBe(JSON.stringify(wantParams)); 62 | } 63 | }; 64 | 65 | export const expectTransitions = ( 66 | receiptTransitions: Array<{ msg: { params: any } }> | undefined, 67 | expectedTransitions: Array<{ 68 | tag: string, 69 | getParams: () => Record>, 70 | }> 71 | ) => { 72 | if (!receiptTransitions && expectedTransitions.length > 0) { 73 | fail("Expected transitions but got none"); 74 | return; 75 | } 76 | 77 | expect(receiptTransitions!.length).toBe(expectedTransitions.length); 78 | 79 | for (const [index, transition] of receiptTransitions!.entries()) { 80 | const { msg } = transition; 81 | expect(expectedTransitions[index]!.tag).toBe(expectedTransitions[index]!.tag); 82 | const wantParams = scillaJSONParams(expectedTransitions[index]!.getParams()); 83 | expect(JSON.stringify(msg.params)).toBe(JSON.stringify(wantParams)); 84 | } 85 | }; 86 | 87 | export const getErrorMsg = (code) => 88 | `Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 ${code}))])`; 89 | 90 | export function runAllTestCases( 91 | testCases: Array, 92 | testedContractAddress: () => string, 93 | txParams: any 94 | ) { 95 | for (const testCase of testCases) { 96 | it(`${testCase.transition}: ${testCase.name}`, async () => { 97 | zilliqa.wallet.setDefault(testCase.getSender()); 98 | const tx: any = await zilliqa.contracts 99 | .at(testedContractAddress()) 100 | .call( 101 | testCase.transition, 102 | scillaJSONParams(testCase.getParams()), 103 | txParams 104 | ); 105 | 106 | if (testCase.want === undefined) { 107 | // Negative Cases 108 | expect(tx.receipt.success).toBe(false); 109 | expect(tx.receipt.exceptions[0].message).toBe( 110 | getErrorMsg(testCase.error) 111 | ); 112 | } else { 113 | // Positive Cases 114 | expect(tx.receipt.success).toBe(true); 115 | expectEvents(tx.receipt.event_logs, testCase.want.events); 116 | expectTransitions(tx.receipt.transitions, testCase.want.transitions); 117 | 118 | const state = await zilliqa.contracts 119 | .at(testedContractAddress()) 120 | .getState(); 121 | 122 | testCase.want.expectState(state); 123 | } 124 | }); 125 | } 126 | } -------------------------------------------------------------------------------- /tests/zrc2/fungible-token-burnable/config.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { Long, BN } from "@zilliqa-js/util"; 3 | import { GAS_PRICE, VERSION } from "../../globalConfig"; 4 | 5 | 6 | const CODE_PATH = "reference-contracts/FungibleToken-Burnable.scilla"; 7 | export const CODE = fs.readFileSync(CODE_PATH).toString(); 8 | 9 | export const FungibleBurnableToken_ERROR = { 10 | CodeIsSender: -1, 11 | CodeInsufficientFunds: -2, 12 | CodeInsufficientAllowance: -3, 13 | }; 14 | 15 | export const TOKEN_NAME = "TEST"; 16 | export const TOKEN_SYMBOL = "T"; 17 | export const TOKEN_DECIMALS = 6; 18 | export const TOKEN_INIT_SUPPLY = 1000000000; 19 | 20 | export const GAS_LIMIT = Long.fromNumber(100000); 21 | 22 | export const TX_PARAMS = { 23 | version: VERSION, 24 | amount: new BN(0), 25 | gasPrice: GAS_PRICE, 26 | gasLimit: GAS_LIMIT, 27 | }; 28 | -------------------------------------------------------------------------------- /tests/zrc2/fungible-token-burnable/fungibleBurnableToken.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "@jest/globals"; 2 | import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; 3 | 4 | import { 5 | getAccounts, 6 | runAllTestCases, 7 | } from "../../testutils"; 8 | 9 | import { 10 | TX_PARAMS, 11 | CODE, 12 | FungibleBurnableToken_ERROR, 13 | TOKEN_NAME, 14 | TOKEN_SYMBOL, 15 | TOKEN_DECIMALS, 16 | TOKEN_INIT_SUPPLY, 17 | } from "./config"; 18 | import { GENESIS_PRIVATE_KEY, JEST_WORKER_ID, zilliqa } from "../../globalConfig"; 19 | 20 | let globalContractAddress: string | undefined; 21 | 22 | let globalTestAccounts: Array<{ 23 | privateKey: string; 24 | address: string; 25 | }> = []; 26 | const CONTRACT_OWNER = 0; 27 | const TOKEN_OWNER = 0; 28 | const STRANGER_1 = 1; 29 | const STRANGER_2 = 2; 30 | const STRANGER_1_INITIAL_TOKENS = 1000; 31 | const getTestAddr = (index) => globalTestAccounts[index]?.address as string; 32 | 33 | beforeAll(async () => { 34 | globalTestAccounts = await getAccounts(4); 35 | 36 | console.table({ 37 | JEST_WORKER_ID, 38 | GENESIS_PRIVATE_KEY, 39 | CONTRACT_OWNER: getTestAddr(CONTRACT_OWNER), 40 | TOKEN_OWNER: getTestAddr(TOKEN_OWNER), 41 | STRANGER_1: getTestAddr(STRANGER_1), 42 | STRANGER_2: getTestAddr(STRANGER_2), 43 | }); 44 | }); 45 | 46 | beforeEach(async () => { 47 | zilliqa.wallet.setDefault(getTestAddr(CONTRACT_OWNER)); 48 | const init = scillaJSONParams({ 49 | _scilla_version: ["Uint32", 0], 50 | contract_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], 51 | name: ["String", TOKEN_NAME], 52 | symbol: ["String", TOKEN_SYMBOL], 53 | decimals: ["Uint32", TOKEN_DECIMALS], 54 | init_supply: ["Uint128", TOKEN_INIT_SUPPLY], 55 | }); 56 | const [contractDeploymentTransaction, contract] = await zilliqa.contracts 57 | .new(CODE, init) 58 | .deploy(TX_PARAMS, 33, 1000, true); 59 | globalContractAddress = contract.address; 60 | 61 | if (globalContractAddress === undefined) { 62 | throw new Error(JSON.stringify({ 63 | message: "Failed to deploy FungibleToken-Burnable contract", 64 | receipt: contractDeploymentTransaction.getReceipt() 65 | })); 66 | } 67 | 68 | let stranger1InitialTokenTransferTx: any = await zilliqa.contracts.at(globalContractAddress).call( 69 | "Transfer", 70 | scillaJSONParams({ 71 | to: ["ByStr20", getTestAddr(STRANGER_1)], 72 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 73 | }), 74 | TX_PARAMS 75 | ); 76 | 77 | if (!stranger1InitialTokenTransferTx.receipt.success) { 78 | throw new Error("Initial transfer fund to STRANGER_1 failed"); 79 | } 80 | }); 81 | 82 | describe("Burn", () => { 83 | runAllTestCases( 84 | [ 85 | { 86 | name: "throws CodeInsufficientFunds if not enough funds", 87 | transition: "Burn", 88 | getSender: () => getTestAddr(STRANGER_1), 89 | getParams: () => ({ 90 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS * 2], 91 | }), 92 | error: FungibleBurnableToken_ERROR.CodeInsufficientFunds, 93 | want: undefined, 94 | }, 95 | { 96 | name: "successfuly burns tokens", 97 | transition: "Burn", 98 | getSender: () => getTestAddr(STRANGER_1), 99 | getParams: () => ({ 100 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 101 | }), 102 | error: undefined, 103 | want: { 104 | expectState: (state) => { 105 | expect( 106 | state.total_supply 107 | ).toEqual(`${TOKEN_INIT_SUPPLY - STRANGER_1_INITIAL_TOKENS}`); 108 | }, 109 | events: [ 110 | { 111 | name: "Burnt", 112 | getParams: () => ({ 113 | burner: ["ByStr20", getTestAddr(STRANGER_1)], 114 | burn_account: ["ByStr20", getTestAddr(STRANGER_1)], 115 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 116 | }), 117 | }, 118 | ], 119 | transitions: [ 120 | { 121 | tag: "BurnSuccessCallBack", 122 | getParams: () => ({ 123 | burner: ["ByStr20", getTestAddr(STRANGER_1)], 124 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 125 | }), 126 | }, 127 | ], 128 | }, 129 | }, 130 | ], 131 | () => globalContractAddress!, 132 | TX_PARAMS 133 | ) 134 | }); 135 | 136 | describe("Transfer", () => { 137 | runAllTestCases( 138 | [ 139 | { 140 | name: "throws CodeInsufficientFunds if not enough funds", 141 | transition: "Transfer", 142 | getSender: () => getTestAddr(STRANGER_1), 143 | getParams: () => ({ 144 | to: ["ByStr20", getTestAddr(STRANGER_2)], 145 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS * 2], 146 | }), 147 | error: FungibleBurnableToken_ERROR.CodeInsufficientFunds, 148 | want: undefined, 149 | }, 150 | { 151 | name: "successfuly transfer tokens", 152 | transition: "Transfer", 153 | getSender: () => getTestAddr(STRANGER_1), 154 | getParams: () => ({ 155 | to: ["ByStr20", getTestAddr(STRANGER_2)], 156 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 157 | }), 158 | error: undefined, 159 | want: { 160 | expectState: (state) => { 161 | expect( 162 | state.total_supply 163 | ).toEqual(`${TOKEN_INIT_SUPPLY}`); 164 | 165 | console.log("balanceeees", state.balances); 166 | }, 167 | events: [ 168 | { 169 | name: "TransferSuccess", 170 | getParams: () => ({ 171 | sender: ["ByStr20", getTestAddr(STRANGER_1)], 172 | recipient: ["ByStr20", getTestAddr(STRANGER_2)], 173 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 174 | }), 175 | }, 176 | ], 177 | transitions: [ 178 | { 179 | tag: "RecipientAcceptTransfer", 180 | getParams: () => ({ 181 | sender: ["ByStr20", getTestAddr(STRANGER_1)], 182 | recipient: ["ByStr20", getTestAddr(STRANGER_2)], 183 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 184 | }), 185 | }, 186 | { 187 | tag: "TransferSuccessCallBack", 188 | getParams: () => ({ 189 | sender: ["ByStr20", getTestAddr(STRANGER_1)], 190 | recipient: ["ByStr20", getTestAddr(STRANGER_2)], 191 | amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], 192 | }), 193 | }, 194 | ], 195 | }, 196 | }, 197 | ], 198 | () => globalContractAddress!, 199 | TX_PARAMS 200 | ) 201 | }); 202 | -------------------------------------------------------------------------------- /tests/zrc6/config.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { Long, BN } from "@zilliqa-js/util"; 3 | import { GAS_PRICE, VERSION } from "../globalConfig"; 4 | 5 | 6 | const CODE_PATH = "reference-contracts/zrc6.scilla"; 7 | export const CODE = fs.readFileSync(CODE_PATH).toString(); 8 | 9 | export const ZRC6_ERROR = { 10 | NotPausedError: -1, 11 | PausedError: -2, 12 | SelfError: -3, 13 | NotContractOwnerError: -4, 14 | NotTokenOwnerError: -5, 15 | NotMinterError: -6, 16 | NotOwnerOrOperatorError: -7, 17 | MinterNotFoundError: -8, 18 | MinterFoundError: -9, 19 | SpenderFoundError: -10, 20 | OperatorNotFoundError: -11, 21 | OperatorFoundError: -12, 22 | NotAllowedToTransferError: -13, 23 | TokenNotFoundError: -14, 24 | InvalidFeeBPSError: -15, 25 | ZeroAddressDestinationError: -16, 26 | ThisAddressDestinationError: -17, 27 | NotContractOwnershipRecipientError: -18, 28 | }; 29 | 30 | export const TOKEN_NAME = "TEST"; 31 | export const TOKEN_SYMBOL = "T"; 32 | export const BASE_URI = "https://creatures-api.zilliqa.com/api/creature/"; 33 | 34 | export const GAS_LIMIT = Long.fromNumber(100000); 35 | 36 | export const TX_PARAMS = { 37 | version: VERSION, 38 | amount: new BN(0), 39 | gasPrice: GAS_PRICE, 40 | gasLimit: GAS_LIMIT, 41 | }; 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "commonjs" /* Specify what module code is generated. */, 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | 68 | /* Interop Constraints */ 69 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 70 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 71 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, 72 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 73 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 74 | 75 | /* Type Checking */ 76 | "strict": true /* Enable all strict type-checking options. */, 77 | "noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied `any` type.. */, 78 | "strictNullChecks": true /* When type checking, take into account `null` and `undefined`. */, 79 | "strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */, 80 | "strictBindCallApply": true /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */, 81 | "strictPropertyInitialization": true /* Check for class properties that are declared but not set in the constructor. */, 82 | "noImplicitThis": true /* Enable error reporting when `this` is given the type `any`. */, 83 | "useUnknownInCatchVariables": true /* Type catch clause variables as 'unknown' instead of 'any'. */, 84 | "alwaysStrict": true /* Ensure 'use strict' is always emitted. */, 85 | "noUnusedLocals": true /* Enable error reporting when a local variables aren't read. */, 86 | "noUnusedParameters": true /* Raise an error when a function parameter isn't read */, 87 | "exactOptionalPropertyTypes": true /* Interpret optional property types as written, rather than adding 'undefined'. */, 88 | "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */, 89 | "noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */, 90 | "noUncheckedIndexedAccess": true /* Include 'undefined' in index signature results */, 91 | "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an override modifier. */, 92 | "noPropertyAccessFromIndexSignature": true /* Enforces using indexed accessors for keys declared using an indexed type */, 93 | "allowUnusedLabels": true /* Disable error reporting for unused labels. */, 94 | "allowUnreachableCode": true /* Disable error reporting for unreachable code. */ 95 | 96 | /* Completeness */ 97 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 98 | // "skipLibCheck": true /* Skip type checking all .d.ts files. */ 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /zrcs/zrc-4.md: -------------------------------------------------------------------------------- 1 | | ZRC | Title | Status | Type | Author | Created (yyyy-mm-dd) | Updated (yyyy-mm-dd) | 2 | | --- | ---------------------------- | ------ | -------- | ---------------------------- | -------------------- | -------------------- | 3 | | 1 | Standard for Multisig Wallet | Draft | Standard | Yeo Te Ye | 2020-04-08 | 2020-04-13 | 4 | 5 | ## I. What is a MultiSig Wallet? 6 | 7 | A multi-signature wallet is a cryptocurrency wallet owned by two or more owners. Whenever a transaction is created, the transaction has to be approved and signed by two or more owners before it can be executed on the blockchain. A multisig wallet can implement various combination of keys: with 2/3 being the most common where 2 signatures are minimally required out of the 3 owners. 8 | 9 | ## II. Abstract 10 | 11 | ZRC-4 defines a minimum interface of a multisig wallet smart contract. The contract holds funds that can be paid out to arbitrary users, provided that enough people in the designated list of owners sign off on the payout. 12 | 13 | The transaction must be added to the contract before signatures can be collected. Once sufficient signatures are collected, the recipient can request for the transaction to be executed and the money paid out. 14 | 15 | ## III. Motivation 16 | 17 | A standard for multisig wallet can serve as an interface for developers and other companies to implement their own multisig wallets to contain their funds in a more secure fashion. The multisig wallet can be used in escrow scenarios and decision making scenarios to prevent misuse of funds. 18 | 19 | ## IV. Specification 20 | 21 | The multisig wallet contract specification describes: 22 | 23 | 1. the global error codes to be declared in the library part of the contract; 24 | 2. the names and types of the immutable and mutable variables (aka `fields`); 25 | 3. the transitions that will allow the changing of values of the mutable variables; 26 | 4. the events to be emitted by them. 27 | 28 | ### A. Error Codes 29 | 30 | The multisig contract define the following constants for use as error codes for the `Error` event. 31 | 32 | | Name | Type | Code | Description | 33 | | ------------------------- | ------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------- | 34 | | `NonOwnerCannotSign` | `Int32` | `-1` | Emit when a non-owner attempts to sign a transaction. | 35 | | `UnknownTransactionId` | `Int32` | `-2` | Emit when a request is made for a transaction which cannot be found. | 36 | | `InsufficientFunds` | `Int32` | `-3` | Emit when there is insufficient balance for token transaction. | 37 | | `NoSignatureListFound` | `Int32` | `-4` | Emit when there are no signatures for a valid transaction request. | 38 | | `AlreadySigned` | `Int32` | `-5` | Emit when an owner attempts to sign a transaction that has already been signed by the same owner. | 39 | | `NotAlreadySigned` | `Int32` | `-6` | Emit when a request is made to revoke a signature on a exisiting transaction in which the sender has not signed. | 40 | | `InvalidContract` | `Int32` | `-7` | Emit when a request is made to an invalid multisig contract. | 41 | | `InvalidAmount` | `Int32` | `-8` | Emit when an owner attempts to send an empty amount to a recipient wallet. | 42 | | `NotEnoughSignatures` | `Int32` | `-9` | Emit when the number of signatures counts is less than the number of signatures required to execute a transaction. | 43 | | `SenderMayNotExecute` | `Int32` | `-10` | Emit when a request is made to execute a transaction in which the sender is neither any of the owners nor the receipent of the transaction. | 44 | | `NonOwnerCannotSubmit` | `Int32` | `-11` | Emit when a non-owner attempts to create a new transaction. | 45 | | `IncorrectSignatureCount` | `Int32` | `-12` | Emit when trying to revoke a signature of an existing transaction in which there are no signatures. | 46 | 47 | ### B. Immutable Variables 48 | 49 | | Name | Type | Description | 50 | | --------------------- | -------------- | ----------------------------------------------------------------------- | 51 | | `owners_list` | `List ByStr20` | The list of owners of the multisig wallet. | 52 | | `required_signatures` | `Uint32` | The number of signatures required to approve and execute a transaction. | 53 | 54 | **Note**: it is a good idea to set `required_signatures` to a value strictly less than the number of owners, so that the remaining owners can retrieve the funds should some owners lose their private keys, or unable or unwilling to sign for new transactions. 55 | 56 | ### C. Mutable Fields 57 | 58 | | Name | Type | Description | 59 | | ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 60 | | `owners` | `Map ByStr20 Bool` | Mapping of multisig wallets owners to a boolean. True indicates an owner. | 61 | | `transactionCount` | `Uint32` | The number of accumulated transactions from contract initialization. | 62 | | `signatures` | `Map Uint32 (Map ByStr20 Bool)` | Mapping from transaction IDs to signees. | 63 | | `signature_counts` | `Map Uint32 Uint32` | Mapping from transaction IDs to accumulated count of signatures. | 64 | | `transactions` | `Map Uint32 Transaction` | Mapping from transaction IDs to a `Transaction` object.
`Transaction` object contains the `recipient`, `amount` and `tag` in the form: `Trans of ByStr20 Uint128 String`. | 65 | 66 | **Note**: Although `owners` is listed as a mutable fields, this multisig wallet contract specification is designed to prevent adding or removing owners. Refer to the section [V. Update Owners or Change Number of Required Signatures](#v.-update-owners-or-change-number-of-required-signatures) for more information. 67 | 68 | ### D. Interface Transitions 69 | 70 | #### 1. SubmitTransaction() 71 | 72 | ``` 73 | (* Creates a transaction request for future signoff *) 74 | transition SubmitTransaction (recipient : ByStr20, amount : Uint128, tag : String) 75 | ``` 76 | 77 | **Arguments:** 78 | | Name | Type | Description | 79 | | ----------- | --------- | ----------- | 80 | | `recipient` | `ByStr20` | Address of the recipient to transfer amount to. | 81 | | `amount` | `Uint128` | Amount of funds to be transferred. | 82 | | `tag` | `String` | Transition name to be invoked. Designed in the scenario of invoking a transition of another contract. Otherwise, the `tag` should be set to `AddFunds`. | 83 | 84 | **Events:** 85 | | | Name | Description | Event Parameters | 86 | | ------------ | --------------------- | -------------------------------------- | ---------------- | 87 | | `_eventname` | `Transaction created` | Transaction is submitted successfully. |
  • `transactionId` : `Uint32`
    Identifier for submitted transaction
  • `recipient` : `ByStr20`
    Address of recipient
  • `amount` : `Uint128`
    Amount of funds to be transferred
  • `tag` : `String`
    Transition name to be invoked
| 88 | | `_eventname` | `Error` | Transaction is not submitted. |
  • emit `NonOwnerCannotSubmit` if the transition is not called by the wallet owners
  • emit `InvalidAmount` if the transition is called with empty `amount`
| 89 | 90 | #### 2. SignTransaction() 91 | 92 | ``` 93 | (* Sign off on an existing transaction *) 94 | transition SignTransaction (transactionId : Uint32) 95 | ``` 96 | 97 | **Arguments:** 98 | | Name | Type | Description | 99 | | --------------- | ------- | ---------------------------------------------------- | 100 | | `transactionId` | `Uint32` | Identifier for the transaction request to be signed. | 101 | 102 | **Events:** 103 | | | Name | Description | Event Parameters | 104 | | ------------- | -------------------- | ----------------------------------- | ---------------- | 105 | | `_eventname` | `Transaction signed` | Transaction is signed successfully. |
  • `transactionId` : `Uint32`
    Identifier for transaction request
| 106 | | `_eventname` | `Error` | Transaction is not signed. |
  • emit `NonOwnerCannotSign` if the transition is not called by the wallet owners
  • emit `UnknownTransactionId` if there are no transaction records for the specified `transitionId`
  • emit `AlreadySigned` if the transaction already has an existing signature of the signee
| 107 | 108 | #### 3. ExecuteTransaction() 109 | 110 | ``` 111 | (* Execute signed-off transaction *) 112 | transition ExecuteTransaction (transactionId : Uint32) 113 | ``` 114 | 115 | **Arguments:** 116 | | Name | Type | Description | 117 | | --------------- | ------- | ------------------------------------------------------ | 118 | | `transactionId` | `Uint32` | Identifier for the transaction request to be executed. | 119 | 120 | **Messages sent:** 121 | | | Name | Description | Callback Parameters | 122 | | ------ | -------------------- | ----------------------------------------------- | ------------------- | 123 | | `_tag` | `ExecuteTransaction` | Provide the sender the status of the execution. |
  • `recipient` : `ByStr20`
    Address of recipient
  • `amount` : `Uint128`
    Amount of funds to be transferred
| 124 | 125 | **Events:** 126 | | | Name | Description | Event Parameters | 127 | | ------------- | ---------------------- | ------------------------------------- | ---------------- | 128 | | `_eventname` | `Transaction executed` | Transaction is executed successfully. |
  • `transactionId` : `Uint32`
    Identifier for transaction request
  • `recipient` : `ByStr20`
    Address of recipient
  • `amount` : `Uint128`
    Amount of funds to be transferred
  • `tag` : `String`
    Transition name to be invoked
| 129 | | `_eventname` | `Error` | Transaction is not executed. |
  • emit `UnknownTransactionId` if there are no transaction records for the specified `transitionId`
  • emit `SenderMayNotExecute` if the transition is neither called by the wallet owners nor recipient
  • emit `InsufficientFunds` if the wallet does not have sufficient funds to transfer over to the recipient
  • emit `NoSignatureListFound` if no signature records exist for the stated transaction record
  • emit `NotEnoughSignatures` if the transaction is executed without fulfilling the minimum number of required signatures
| 130 | 131 | #### 4. RevokeSignature() 132 | 133 | ``` 134 | (* Revoke signature of existing transaction, if it has not yet been executed. *) 135 | transition RevokeSignature (transactionId : Uint32) 136 | ``` 137 | 138 | **Arguments:** 139 | | Name | Type | Description | 140 | | --------------- | ------- | -------------------------------------------------------------------- | 141 | | `transactionId` | `Uint32` | Transaction identifier in which the signature is to be removed from. | 142 | 143 | **Events:** 144 | | | Name | Description | Event Parameters | 145 | | ------------- | ------------------- | ------------------------------------------------------- | ---------------- | 146 | | `_eventname` | `Signature revoked` | Signature is revoked from the transaction successfully. |
  • `transactionId` : `Uint32`
    Identifier for transaction request to revoke the signature from
| 147 | | `_eventname` | `Error` | Signature is not revoked. |
  • emit `NotAlreadySigned` if sender's signature is not found in the signature records for the specific `transactionId`
  • emit `IncorrectSignatureCount` if there do not exist any signatures for the existing transaction record
148 | 149 | #### 5. AddFunds() 150 | 151 | ``` 152 | (* Add native funds to wallet *) 153 | transition AddFunds () 154 | ``` 155 | 156 | ## V. Update Owners or Change Number of Required Signatures 157 | 158 | This multisig wallet contract is designed to prevent adding or removing owners, or changing the number of required signatures. 159 | 160 | The proposed design for performing the aforementioned changes is: 161 | 162 | 1. Deploy a new wallet with the owners and number of required signatures adjusted. 163 | 2. On the **old wallet**, invoke `SubmitTransaction` transition with the following parameters: 164 | - `recipient` : Address of new wallet 165 | - `amount` : \_balance of old wallet 166 | - `tag` : `AddFunds` 167 | 3. Next, on the **old wallet**, have the various owners invoke `SignTransaction` transition with the following parameters until the minimal required signature is reached: 168 | - `transactionId` : 169 | 4. Lastly, on the **old wallet**, have one of the owners invoke `ExecuteTransaction` transition. All the existing balance of the **old wallet** would be transferred to the new wallet from (1). 170 | 171 | ## VI. Existing Implementation(s) 172 | 173 | - [MultiSig Wallet Reference contract](../reference-contracts/multisig_wallet.scilla) 174 | 175 | To test the reference contract, simply go to the [`example/zrc4`](../example/zrc4) folder and run one of the JS scripts. 176 | 177 | ## VII. Copyright 178 | 179 | Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). 180 | -------------------------------------------------------------------------------- /zrcs/zrc-5.md: -------------------------------------------------------------------------------- 1 | | ZRC | Title | Status | Type | Author | Created (yyyy-mm-dd) | Updated (yyyy-mm-dd) | 2 | | --- | ----------------------------- | ----------- | -------- | -------------------------------- | -------------------- | -------------------- | 3 | | 5 | Convention for Deposit of ZIL | Implemented | Standard | Jun Hao Tan | 2020-06-25 | 2021-11-12 | 4 | 5 | ## I. What is ZIL? 6 | 7 | ZIL is the native token for [Zilliqa](https://www.zilliqa.com/) blockchain. One can store ZIL inside any Zilliqa address, including smart contract address. 8 | 9 | ## II. Abstract 10 | 11 | ZRC-5 defines a convention for an interface that a smart contract should implement should it decide to accept ZIL. 12 | 13 | ## III. Motivation 14 | 15 | For a smart contract to accept incoming ZIL, it needs to do so explicitly using the `accept` statement. As such, any transition that does not have the `accept` statement will not be able to accept any incoming ZIL transfer. 16 | 17 | However, there is currently no naming convention for transitions that can accept ZIL. As a result, cryptocurrency exchanges or cryptocurrency wallet providers do not know which `_tag` to set, should they wish to transfer ZIL to a contract address. 18 | 19 | By having a naming convention for transitions that can accept ZIL, one can easily transfer ZIL to a contract that follows this convention, thereby improving composability. 20 | 21 | ## IV. Specification 22 | 23 | The deposit of ZIL specification describes: 24 | 25 | 1. the naming convention of the transition 26 | 2. mandatory instruction in the transition 27 | 28 | ### A. Naming of transition 29 | 30 | For a contract that wishes to conditionally or unconditionally accept ZIL, it should implement a transition named `AddFunds` that does not take any parameters. 31 | 32 | ### B. Mandatory instruction 33 | 34 | Within the `AddFunds` transition, there should be an `accept` instruction. 35 | 36 | ### C. Conditional acceptance of ZIL 37 | 38 | Smart contract developers are free to introduce any programmatic logic to conditionally accept ZIL in `AddFunds` transition. Smart contract developers can also use `accept` instruction in other transitions, however, such transitions may reduce composability of the smart contract. 39 | 40 | ### D. Sample implementation 41 | 42 | This is a sample implementation of `AddFunds` transition that unconditionally accepts ZIL. 43 | 44 | ``` 45 | transition AddFunds () 46 | accept 47 | end 48 | ``` 49 | 50 | ## V. Existing Implementation(s) 51 | 52 | - [MultiSig Wallet Reference contract](../reference-contracts/multisig_wallet.scilla#L406) 53 | 54 | To test the reference contract, simply go to the [`example/zrc4`](../example/zrc4) folder and run `node add_funds.js`. 55 | 56 | ## VI. Copyright 57 | 58 | Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). 59 | --------------------------------------------------------------------------------