From 33f3bf2c325430b2c9c4dac461f2b598a34777f9 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Fri, 2 Feb 2024 15:17:37 -0700 Subject: [PATCH 1/3] add fetch to ckbtc example --- examples/ckbtc/dfx.json | 3 +- examples/ckbtc/package.json | 7 +- examples/ckbtc/wallet/backend/index.ts | 196 +++++++++++++++++++------ 3 files changed, 160 insertions(+), 46 deletions(-) diff --git a/examples/ckbtc/dfx.json b/examples/ckbtc/dfx.json index 8186afbf4a..e17d26fc2d 100644 --- a/examples/ckbtc/dfx.json +++ b/examples/ckbtc/dfx.json @@ -31,7 +31,8 @@ "node_compatibility": true, "output": "wallet/frontend/dfx_generated/wallet_backend" }, - "env": ["CK_BTC_PRINCIPAL", "MINTER_PRINCIPAL"] + "env": ["CK_BTC_PRINCIPAL", "MINTER_PRINCIPAL", "AZLE_TEST_FETCH"], + "assets": [["minter/minter.did", "src/minter.did"]] }, "wallet_frontend": { "type": "assets", diff --git a/examples/ckbtc/package.json b/examples/ckbtc/package.json index d6d73fd7c3..a0ab11b2a7 100644 --- a/examples/ckbtc/package.json +++ b/examples/ckbtc/package.json @@ -18,9 +18,10 @@ "deploy:wallet_frontend": "dfx deploy wallet_frontend --specified-id ryjl3-tyaaa-aaaaa-aaaba-cai", "frontend": "npx open-cli http://ryjl3-tyaaa-aaaaa-aaaba-cai.localhost:8000/", "mint": ".bitcoin/bin/bitcoin-cli -conf=$(pwd)/.bitcoin.conf generatetoaddress 1 $npm_config_address", - "pretest": "ts-node --transpile-only --ignore=false test/pretest.ts", - "test": "ts-node --transpile-only --ignore=false test/test.ts", - "build": "cd wallet/frontend && npm run build" + "build": "cd wallet/frontend && npm run build", + "pre_tests": "ts-node --transpile-only --ignore=false test/pretest.ts", + "tests": "npm run pre_tests && ts-node --transpile-only --ignore=false test/test.ts", + "test": "AZLE_TEST_FETCH=false npm run tests && AZLE_TEST_FETCH=true npm run tests" }, "dependencies": { "azle": "0.20.1" diff --git a/examples/ckbtc/wallet/backend/index.ts b/examples/ckbtc/wallet/backend/index.ts index 20e95d36eb..0f0af48df8 100644 --- a/examples/ckbtc/wallet/backend/index.ts +++ b/examples/ckbtc/wallet/backend/index.ts @@ -13,6 +13,7 @@ import { nat, nat64, postUpgrade, + serialize, text, update } from 'azle'; @@ -29,66 +30,177 @@ export default Canister({ init: init([], setupCanisters), postUpgrade: postUpgrade([], setupCanisters), getBalance: update([], nat64, async () => { - return await ic.call(ckBTC.icrc1_balance_of, { - args: [ + if (process.env.AZLE_TEST_FETCH === 'true') { + const response = await fetch( + `icp://${(ckBTC as any).principal.toText()}/icrc1_balance_of`, { - owner: ic.id(), - subaccount: Some( - padPrincipalWithZeros(ic.caller().toUint8Array()) - ) + body: serialize({ + candidPath: `/candid/icrc.did`, + args: [ + { + owner: ic.id(), + subaccount: [ + padPrincipalWithZeros( + ic.caller().toUint8Array() + ) + ] + } + ] + }) } - ] - }); + ); + const responseJson = await response.json(); + + return responseJson; + } else { + return await ic.call(ckBTC.icrc1_balance_of, { + args: [ + { + owner: ic.id(), + subaccount: Some( + padPrincipalWithZeros(ic.caller().toUint8Array()) + ) + } + ] + }); + } }), updateBalance: update([], UpdateBalanceResult, async () => { - return await ic.call(minter.update_balance, { - args: [ + if (process.env.AZLE_TEST_FETCH === 'true') { + const response = await fetch( + `icp://${(minter as any).principal.toText()}/update_balance`, { - owner: Some(ic.id()), - subaccount: Some( - padPrincipalWithZeros(ic.caller().toUint8Array()) - ) + body: serialize({ + candidPath: `/src/minter.did`, + args: [ + { + owner: [ic.id()], + subaccount: [ + padPrincipalWithZeros( + ic.caller().toUint8Array() + ) + ] + } + ] + }) } - ] - }); + ); + const responseJson = await response.json(); + + return responseJson; + } else { + return await ic.call(minter.update_balance, { + args: [ + { + owner: Some(ic.id()), + subaccount: Some( + padPrincipalWithZeros(ic.caller().toUint8Array()) + ) + } + ] + }); + } }), getDepositAddress: update([], text, async () => { - return await ic.call(minter.get_btc_address, { - args: [ + if (process.env.AZLE_TEST_FETCH === 'true') { + const response = await fetch( + `icp://${(minter as any).principal.toText()}/get_btc_address`, { - owner: Some(ic.id()), - subaccount: Some( - padPrincipalWithZeros(ic.caller().toUint8Array()) - ) + body: serialize({ + candidPath: `/src/minter.did`, + args: [ + { + owner: [ic.id()], + subaccount: [ + padPrincipalWithZeros( + ic.caller().toUint8Array() + ) + ] + } + ] + }) } - ] - }); + ); + const responseJson = await response.json(); + + return responseJson; + } else { + return await ic.call(minter.get_btc_address, { + args: [ + { + owner: Some(ic.id()), + subaccount: Some( + padPrincipalWithZeros(ic.caller().toUint8Array()) + ) + } + ] + }); + } }), transfer: update( [text, nat], Result(nat, TransferError), async (to, amount) => { - return await ic.call(ckBTC.icrc1_transfer, { - args: [ + if (process.env.AZLE_TEST_FETCH === 'true') { + const response = await fetch( + `icp://${(ckBTC as any).principal.toText()}/icrc1_transfer`, { - from_subaccount: Some( - padPrincipalWithZeros(ic.caller().toUint8Array()) - ), - to: { - owner: ic.id(), - subaccount: Some( + body: serialize({ + candidPath: `/candid/icrc.did`, + args: [ + { + from_subaccount: [ + padPrincipalWithZeros( + ic.caller().toUint8Array() + ) + ], + to: { + owner: ic.id(), + subaccount: [ + padPrincipalWithZeros( + Principal.fromText( + to + ).toUint8Array() + ) + ] + }, + amount, + fee: [], + memo: [], + created_at_time: [] + } + ] + }) + } + ); + const responseJson = await response.json(); + + return responseJson; + } else { + return await ic.call(ckBTC.icrc1_transfer, { + args: [ + { + from_subaccount: Some( padPrincipalWithZeros( - Principal.fromText(to).toUint8Array() + ic.caller().toUint8Array() ) - ) - }, - amount, - fee: None, - memo: None, - created_at_time: None - } - ] - }); + ), + to: { + owner: ic.id(), + subaccount: Some( + padPrincipalWithZeros( + Principal.fromText(to).toUint8Array() + ) + ) + }, + amount, + fee: None, + memo: None, + created_at_time: None + } + ] + }); + } } ) }); From 94dc540a452fd926b15c2a228f08409b1f858181 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Mon, 5 Feb 2024 14:43:41 -0700 Subject: [PATCH 2/3] add fetch to bitcoin example --- canisters/management/bitcoin.ts | 6 +-- canisters/management/ic.did | 1 + examples/bitcoin/dfx.json | 3 +- examples/bitcoin/src/index.ts | 86 +++++++++++++++++++------------- examples/bitcoin/test/pretest.ts | 2 - examples/bitcoin/test/test.ts | 24 ++++----- examples/ckbtc/package.json | 2 +- 7 files changed, 68 insertions(+), 56 deletions(-) diff --git a/canisters/management/bitcoin.ts b/canisters/management/bitcoin.ts index 0c44fbc1a5..e374ad0a8c 100644 --- a/canisters/management/bitcoin.ts +++ b/canisters/management/bitcoin.ts @@ -28,9 +28,9 @@ export const Satoshi = nat64; export type Satoshi = nat64; export const BitcoinNetwork = Variant({ - Mainnet: Null, - Regtest: Null, - Testnet: Null + mainnet: Null, + regtest: Null, + testnet: Null }); export type BitcoinNetwork = typeof BitcoinNetwork.tsType; diff --git a/canisters/management/ic.did b/canisters/management/ic.did index 88cd70dca8..7eab9045b0 100644 --- a/canisters/management/ic.did +++ b/canisters/management/ic.did @@ -81,6 +81,7 @@ type satoshi = nat64; type bitcoin_network = variant { mainnet; testnet; + regtest; }; type bitcoin_address = text; diff --git a/examples/bitcoin/dfx.json b/examples/bitcoin/dfx.json index b866a35758..0c1e281301 100644 --- a/examples/bitcoin/dfx.json +++ b/examples/bitcoin/dfx.json @@ -10,7 +10,8 @@ "declarations": { "output": "test/dfx_generated/bitcoin", "node_compatibility": true - } + }, + "env": ["AZLE_TEST_FETCH"] } }, "networks": { diff --git a/examples/bitcoin/src/index.ts b/examples/bitcoin/src/index.ts index 3ef821f36a..d26cd4e3c0 100644 --- a/examples/bitcoin/src/index.ts +++ b/examples/bitcoin/src/index.ts @@ -1,7 +1,6 @@ -import { blob, bool, Canister, ic, None, text, update, Vec } from 'azle'; +import { blob, bool, Canister, serialize, text, update, Vec } from 'azle'; import { GetUtxosResult, - managementCanister, MillisatoshiPerByte, Satoshi } from 'azle/canisters/management'; @@ -12,41 +11,56 @@ const BITCOIN_CYCLE_COST_PER_TRANSACTION_BYTE = 20_000_000n; export default Canister({ getBalance: update([text], Satoshi, async (address) => { - return await ic.call(managementCanister.bitcoin_get_balance, { - args: [ - { - address, - min_confirmations: None, - network: { Regtest: null } - } - ], - cycles: BITCOIN_API_CYCLE_COST - }); - }), - getCurrentFeePercentiles: update([], Vec(MillisatoshiPerByte), async () => { - return await ic.call( - managementCanister.bitcoin_get_current_fee_percentiles, - { + const response = await fetch(`icp://aaaaa-aa/bitcoin_get_balance`, { + body: serialize({ args: [ { - network: { Regtest: null } + address, + min_confirmations: [], + network: { regtest: null } } ], cycles: BITCOIN_API_CYCLE_COST + }) + }); + const responseJson = await response.json(); + + return responseJson; + }), + getCurrentFeePercentiles: update([], Vec(MillisatoshiPerByte), async () => { + const response = await fetch( + `icp://aaaaa-aa/bitcoin_get_current_fee_percentiles`, + { + body: serialize({ + args: [ + { + network: { regtest: null } + } + ], + cycles: BITCOIN_API_CYCLE_COST + }) } ); + const responseJson = await response.json(); + + return responseJson; }), getUtxos: update([text], GetUtxosResult, async (address) => { - return await ic.call(managementCanister.bitcoin_get_utxos, { - args: [ - { - address, - filter: None, - network: { Regtest: null } - } - ], - cycles: BITCOIN_API_CYCLE_COST + const response = await fetch(`icp://aaaaa-aa/bitcoin_get_utxos`, { + body: serialize({ + args: [ + { + address, + filter: [], + network: { regtest: null } + } + ], + cycles: BITCOIN_API_CYCLE_COST + }) }); + const responseJson = await response.json(); + + return responseJson; }), sendTransaction: update([blob], bool, async (transaction) => { const transactionFee = @@ -54,14 +68,16 @@ export default Canister({ BigInt(transaction.length) * BITCOIN_CYCLE_COST_PER_TRANSACTION_BYTE; - await ic.call(managementCanister.bitcoin_send_transaction, { - args: [ - { - transaction, - network: { Regtest: null } - } - ], - cycles: transactionFee + await fetch(`icp://aaaaa-aa/bitcoin_send_transaction`, { + body: serialize({ + args: [ + { + transaction, + network: { regtest: null } + } + ], + cycles: transactionFee + }) }); return true; diff --git a/examples/bitcoin/test/pretest.ts b/examples/bitcoin/test/pretest.ts index 320939789e..fc14f8eaf9 100644 --- a/examples/bitcoin/test/pretest.ts +++ b/examples/bitcoin/test/pretest.ts @@ -1,8 +1,6 @@ import { execSync } from 'child_process'; async function pretest() { - await new Promise((resolve) => setTimeout(resolve, 5000)); - execSync(`rm -rf .bitcoin/regtest`, { stdio: 'inherit' }); diff --git a/examples/bitcoin/test/test.ts b/examples/bitcoin/test/test.ts index 8842bf1ce5..1abe002f5d 100644 --- a/examples/bitcoin/test/test.ts +++ b/examples/bitcoin/test/test.ts @@ -1,9 +1,13 @@ -import { ok, getCanisterId, runTests, Test } from 'azle/test'; +import { getCanisterId, runTests, Test } from 'azle/test'; import { createActor } from './dfx_generated/bitcoin'; import { wallets } from './wallets'; import { impureSetup, whileRunningBitcoinDaemon } from './setup'; import { bitcoinCli } from './bitcoin_cli'; +const BLOCK_REWARD = 5_000_000_000n; +const BLOCKS_MINED_IN_SETUP = 101n; +const EXPECTED_BALANCE_AFTER_SETUP = BLOCK_REWARD * BLOCKS_MINED_IN_SETUP; + const bitcoinCanister = createActor(getCanisterId('bitcoin'), { agentOptions: { host: 'http://127.0.0.1:8000' @@ -38,16 +42,8 @@ function testCanisterFunctionality() { wallets.alice.p2wpkh ); - const blockReward = 5_000_000_000n; - const blocksMinedInSetup = 101n; - const expectedBalance = blockReward * blocksMinedInSetup; - - // TODO remove this after testing - console.log('result', result); - console.log('expectedBalance', expectedBalance); - return { - Ok: result === expectedBalance + Ok: result === EXPECTED_BALANCE_AFTER_SETUP }; } }, @@ -76,7 +72,7 @@ function testCanisterFunctionality() { { name: 'sendTransaction', test: async () => { - const balance_before_transaction = + const receivedBeforeTransaction = bitcoinCli.getReceivedByAddress(wallets.bob.p2wpkh); const tx_bytes = hex_string_to_bytes(state.signedTxHex); @@ -88,14 +84,14 @@ function testCanisterFunctionality() { // Wait for generated block to be pulled into replica await new Promise((resolve) => setTimeout(resolve, 5000)); - const balance_after_transaction = + const receivedAfterTransaction = bitcoinCli.getReceivedByAddress(wallets.bob.p2wpkh, 0); return { Ok: result === true && - balance_before_transaction === 0 && - balance_after_transaction === 1 + receivedBeforeTransaction === 0 && + receivedAfterTransaction === 1 }; } } diff --git a/examples/ckbtc/package.json b/examples/ckbtc/package.json index a0ab11b2a7..2453b083e5 100644 --- a/examples/ckbtc/package.json +++ b/examples/ckbtc/package.json @@ -21,7 +21,7 @@ "build": "cd wallet/frontend && npm run build", "pre_tests": "ts-node --transpile-only --ignore=false test/pretest.ts", "tests": "npm run pre_tests && ts-node --transpile-only --ignore=false test/test.ts", - "test": "AZLE_TEST_FETCH=false npm run tests && AZLE_TEST_FETCH=true npm run tests" + "test": "AZLE_TEST_FETCH=true npm run tests" }, "dependencies": { "azle": "0.20.1" From 31ad886f32022aaf85d38c101fe67d83f0f64481 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Thu, 8 Feb 2024 12:22:27 -0700 Subject: [PATCH 3/3] pr fixes --- examples/bitcoin/test/test.ts | 10 ++++----- examples/ckbtc/dfx.json | 2 +- examples/ckbtc/package.json | 2 +- examples/ckbtc/wallet/backend/index.ts | 28 ++++++++++++++++++++------ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/examples/bitcoin/test/test.ts b/examples/bitcoin/test/test.ts index 1abe002f5d..ce89d63d18 100644 --- a/examples/bitcoin/test/test.ts +++ b/examples/bitcoin/test/test.ts @@ -4,10 +4,6 @@ import { wallets } from './wallets'; import { impureSetup, whileRunningBitcoinDaemon } from './setup'; import { bitcoinCli } from './bitcoin_cli'; -const BLOCK_REWARD = 5_000_000_000n; -const BLOCKS_MINED_IN_SETUP = 101n; -const EXPECTED_BALANCE_AFTER_SETUP = BLOCK_REWARD * BLOCKS_MINED_IN_SETUP; - const bitcoinCanister = createActor(getCanisterId('bitcoin'), { agentOptions: { host: 'http://127.0.0.1:8000' @@ -42,8 +38,12 @@ function testCanisterFunctionality() { wallets.alice.p2wpkh ); + const blockReward = 5_000_000_000n; + const blocksMinedInSetup = 101n; + const expectedBalance = blockReward * blocksMinedInSetup; + return { - Ok: result === EXPECTED_BALANCE_AFTER_SETUP + Ok: result === expectedBalance }; } }, diff --git a/examples/ckbtc/dfx.json b/examples/ckbtc/dfx.json index e17d26fc2d..613a166184 100644 --- a/examples/ckbtc/dfx.json +++ b/examples/ckbtc/dfx.json @@ -32,7 +32,7 @@ "output": "wallet/frontend/dfx_generated/wallet_backend" }, "env": ["CK_BTC_PRINCIPAL", "MINTER_PRINCIPAL", "AZLE_TEST_FETCH"], - "assets": [["minter/minter.did", "src/minter.did"]] + "assets": [["minter/minter.did", "minter/minter.did"]] }, "wallet_frontend": { "type": "assets", diff --git a/examples/ckbtc/package.json b/examples/ckbtc/package.json index 2453b083e5..a0ab11b2a7 100644 --- a/examples/ckbtc/package.json +++ b/examples/ckbtc/package.json @@ -21,7 +21,7 @@ "build": "cd wallet/frontend && npm run build", "pre_tests": "ts-node --transpile-only --ignore=false test/pretest.ts", "tests": "npm run pre_tests && ts-node --transpile-only --ignore=false test/test.ts", - "test": "AZLE_TEST_FETCH=true npm run tests" + "test": "AZLE_TEST_FETCH=false npm run tests && AZLE_TEST_FETCH=true npm run tests" }, "dependencies": { "azle": "0.20.1" diff --git a/examples/ckbtc/wallet/backend/index.ts b/examples/ckbtc/wallet/backend/index.ts index 0f0af48df8..b98569061e 100644 --- a/examples/ckbtc/wallet/backend/index.ts +++ b/examples/ckbtc/wallet/backend/index.ts @@ -32,7 +32,7 @@ export default Canister({ getBalance: update([], nat64, async () => { if (process.env.AZLE_TEST_FETCH === 'true') { const response = await fetch( - `icp://${(ckBTC as any).principal.toText()}/icrc1_balance_of`, + `icp://${getCkBtcPrincipal()}/icrc1_balance_of`, { body: serialize({ candidPath: `/candid/icrc.did`, @@ -68,10 +68,10 @@ export default Canister({ updateBalance: update([], UpdateBalanceResult, async () => { if (process.env.AZLE_TEST_FETCH === 'true') { const response = await fetch( - `icp://${(minter as any).principal.toText()}/update_balance`, + `icp://${getMinterPrincipal()}/update_balance`, { body: serialize({ - candidPath: `/src/minter.did`, + candidPath: `/minter/minter.did`, args: [ { owner: [ic.id()], @@ -104,10 +104,10 @@ export default Canister({ getDepositAddress: update([], text, async () => { if (process.env.AZLE_TEST_FETCH === 'true') { const response = await fetch( - `icp://${(minter as any).principal.toText()}/get_btc_address`, + `icp://${getMinterPrincipal()}/get_btc_address`, { body: serialize({ - candidPath: `/src/minter.did`, + candidPath: `/minter/minter.did`, args: [ { owner: [ic.id()], @@ -143,7 +143,7 @@ export default Canister({ async (to, amount) => { if (process.env.AZLE_TEST_FETCH === 'true') { const response = await fetch( - `icp://${(ckBTC as any).principal.toText()}/icrc1_transfer`, + `icp://${getCkBtcPrincipal()}/icrc1_transfer`, { body: serialize({ candidPath: `/candid/icrc.did`, @@ -226,3 +226,19 @@ function setupCanisters() { ) ); } + +function getCkBtcPrincipal(): string { + if (process.env.CK_BTC_PRINCIPAL !== undefined) { + return process.env.CK_BTC_PRINCIPAL; + } + + throw new Error(`process.env.CK_BTC_PRINCIPAL is not defined`); +} + +function getMinterPrincipal(): string { + if (process.env.MINTER_PRINCIPAL !== undefined) { + return process.env.MINTER_PRINCIPAL; + } + + throw new Error(`process.env.MINTER_PRINCIPAL is not defined`); +}