├── .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 | [](https://app.travis-ci.com/Zilliqa/ZRC) [](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 |
--------------------------------------------------------------------------------