Skip to content

Commit

Permalink
Merge pull request #1602 from demergent-labs/ledger_fetch_example
Browse files Browse the repository at this point in the history
Ledger fetch example
  • Loading branch information
lastmjs authored Feb 1, 2024
2 parents f3e54fd + c2fafa0 commit 5d411eb
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 50 deletions.
3 changes: 2 additions & 1 deletion examples/ledger_canister/dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"output": "test/dfx_generated/ledger_canister",
"node_compatibility": true
},
"env": ["ICP_CANISTER_PRINCIPAL"]
"env": ["ICP_CANISTER_PRINCIPAL", "AZLE_TEST_FETCH"],
"assets": [["src/icp_ledger/ledger.public.did", "src/ledger.did"]]
},
"icp_ledger": {
"type": "custom",
Expand Down
5 changes: 3 additions & 2 deletions examples/ledger_canister/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"scripts": {
"pretest": "ts-node --transpile-only --ignore=false test/pretest.ts",
"test": "ts-node --transpile-only --ignore=false test/test.ts"
"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.19.0"
Expand Down
16 changes: 8 additions & 8 deletions examples/ledger_canister/src/ledger_canister/index.did
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
service: () -> {
executeTransfer: (text, nat64, nat64, opt nat64) -> (variant {Ok:nat64; Err:variant {TxTooOld:record {allowed_window_nanos:nat64}; BadFee:record {expected_fee:record {e8s:nat64}}; TxDuplicate:record {duplicate_of:nat64}; TxCreatedInFuture; InsufficientFunds:record {balance:record {e8s:nat64}}}}) ;
getAccountBalance: (text) -> (record {e8s:nat64}) ;
executeTransfer: (text, nat64, nat64, opt nat64) -> (variant {Ok:nat64; Err:variant {TxTooOld:record {allowed_window_nanos:nat64}; BadFee:record {expected_fee:record {e8s:nat64}}; TxDuplicate:record {duplicate_of:nat64}; TxCreatedInFuture; InsufficientFunds:record {balance:record {e8s:nat64}}}});
getAccountBalance: (text) -> (record {e8s:nat64});
getAddressFromPrincipal: (principal) -> (text) query;
getArchives: () -> (record {archives:vec record {canister_id:principal}}) ;
getBlocks: (record {start:nat64; length:nat64}) -> (record {certificate:opt vec nat8; blocks:vec record {transaction:record {memo:nat64; operation:opt variant {Burn:record {from:vec nat8; amount:record {e8s:nat64}}; Mint:record {to:vec nat8; amount:record {e8s:nat64}}; Transfer:record {to:vec nat8; fee:record {e8s:nat64}; from:vec nat8; amount:record {e8s:nat64}}}; created_at_time:record {timestamp_nanos:nat64}}; timestamp:record {timestamp_nanos:nat64}; parent_hash:opt vec nat8}; chain_length:nat64; first_block_index:nat64; archived_blocks:vec record {callback:func (record {start:nat64; length:nat64}) -> (variant {Ok:record {blocks:vec record {transaction:record {memo:nat64; operation:opt variant {Burn:record {from:vec nat8; amount:record {e8s:nat64}}; Mint:record {to:vec nat8; amount:record {e8s:nat64}}; Transfer:record {to:vec nat8; fee:record {e8s:nat64}; from:vec nat8; amount:record {e8s:nat64}}}; created_at_time:record {timestamp_nanos:nat64}}; timestamp:record {timestamp_nanos:nat64}; parent_hash:opt vec nat8}}; Err:variant {BadFirstBlockIndex:record {requested_index:nat64; first_valid_index:nat64}; Other:record {error_message:text; error_code:nat64}}}) query; start:nat64; length:nat64}}) ;
getDecimals: () -> (nat32) ;
getName: () -> (text) ;
getSymbol: () -> (text) ;
getTransferFee: () -> (record {transfer_fee:record {e8s:nat64}}) ;
getArchives: () -> (record {archives:vec record {canister_id:principal}});
getBlocks: (record {start:nat64; length:nat64}) -> (record {certificate:opt vec nat8; blocks:vec record {transaction:record {memo:nat64; operation:opt variant {Burn:record {from:vec nat8; amount:record {e8s:nat64}}; Mint:record {to:vec nat8; amount:record {e8s:nat64}}; Transfer:record {to:vec nat8; fee:record {e8s:nat64}; from:vec nat8; amount:record {e8s:nat64}}}; created_at_time:record {timestamp_nanos:nat64}}; timestamp:record {timestamp_nanos:nat64}; parent_hash:opt vec nat8}; chain_length:nat64; first_block_index:nat64; archived_blocks:vec record {callback:func (record {start:nat64; length:nat64}) -> (variant {Ok:record {blocks:vec record {transaction:record {memo:nat64; operation:opt variant {Burn:record {from:vec nat8; amount:record {e8s:nat64}}; Mint:record {to:vec nat8; amount:record {e8s:nat64}}; Transfer:record {to:vec nat8; fee:record {e8s:nat64}; from:vec nat8; amount:record {e8s:nat64}}}; created_at_time:record {timestamp_nanos:nat64}}; timestamp:record {timestamp_nanos:nat64}; parent_hash:opt vec nat8}}; Err:variant {BadFirstBlockIndex:record {requested_index:nat64; first_valid_index:nat64}; Other:record {error_message:text; error_code:nat64}}}) query; start:nat64; length:nat64}});
getDecimals: () -> (nat32);
getName: () -> (text);
getSymbol: () -> (text);
getTransferFee: () -> (record {transfer_fee:record {e8s:nat64}});
}
254 changes: 215 additions & 39 deletions examples/ledger_canister/src/ledger_canister/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import {
Canister,
ic,
init,
nat16,
nat32,
nat64,
None,
Opt,
Principal,
query,
serialize,
Some,
text,
update
Expand All @@ -16,6 +18,7 @@ import {
Address,
Archives,
binaryAddressFromAddress,
Block,
GetBlocksArgs,
hexAddressFromPrincipal,
Ledger,
Expand All @@ -29,78 +32,251 @@ let icpCanister: typeof Ledger;

export default Canister({
init: init([], () => {
icpCanister = Ledger(
Principal.fromText(
process.env.ICP_CANISTER_PRINCIPAL ??
ic.trap('process.env.ICP_CANISTER_PRINCIPAL is undefined')
)
);
icpCanister = Ledger(Principal.fromText(getIcpCanisterPrincipal()));
}),
executeTransfer: update(
[Address, nat64, nat64, Opt(nat64)],
TransferResult,
async (to, amount, fee, createdAtTime) => {
return await ic.call(icpCanister.transfer, {
args: [
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/transfer`,
{
memo: 0n,
amount: {
e8s: amount
},
fee: {
e8s: fee
},
from_subaccount: None,
to: binaryAddressFromAddress(to),
created_at_time:
'None' in createdAtTime
? None
: Some({ timestamp_nanos: createdAtTime.Some })
body: serialize({
candidPath: `/src/ledger.did`,
args: [
{
memo: 0n,
amount: {
e8s: amount
},
fee: {
e8s: fee
},
from_subaccount: [],
to: binaryAddressFromAddress(to),
created_at_time:
'None' in createdAtTime
? []
: [
{
timestamp_nanos:
createdAtTime.Some
}
]
}
]
})
}
]
});
);

return await response.json();
} else {
return await ic.call(icpCanister.transfer, {
args: [
{
memo: 0n,
amount: {
e8s: amount
},
fee: {
e8s: fee
},
from_subaccount: None,
to: binaryAddressFromAddress(to),
created_at_time:
'None' in createdAtTime
? None
: Some({
timestamp_nanos: createdAtTime.Some
})
}
]
});
}
}
),
getAccountBalance: update([Address], Tokens, async (address) => {
return await ic.call(icpCanister.account_balance, {
args: [
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/account_balance`,
{
account: binaryAddressFromAddress(address)
body: serialize({
candidPath: `/src/ledger.did`,
args: [
{
account: binaryAddressFromAddress(address)
}
]
})
}
]
});
);

return await response.json();
} else {
return await ic.call(icpCanister.account_balance, {
args: [
{
account: binaryAddressFromAddress(address)
}
]
});
}
}),
getTransferFee: update([], TransferFee, async () => {
return await ic.call(icpCanister.transfer_fee, { args: [{}] });
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/transfer_fee`,
{
body: serialize({
candidPath: `/src/ledger.did`,
args: [{}]
})
}
);

return await response.json();
} else {
return await ic.call(icpCanister.transfer_fee, { args: [{}] });
}
}),
getBlocks: update(
[GetBlocksArgs],
QueryBlocksResponse,
async (getBlocksArgs) => {
return await ic.call(icpCanister.query_blocks, {
args: [getBlocksArgs]
});
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/query_blocks`,
{
body: serialize({
candidPath: `/src/ledger.did`,
args: [getBlocksArgs]
})
}
);
const {
archived_blocks,
blocks,
certificate,
chain_length,
first_block_index
} = await response.json();
const azleBlocks = blocks.map((block: any) => {
const { parent_hash, timestamp, transaction } = block;
const { created_at_time, memo, operation } = transaction;

return {
parent_hash: agentOptToAzleOpt(parent_hash),
timestamp,
transaction: {
created_at_time,
memo,
operation: agentOptToAzleOpt(operation)
}
};
});

return {
archived_blocks,
blocks: azleBlocks,
certificate: agentOptToAzleOpt(certificate),
chain_length,
first_block_index
};
} else {
return await ic.call(icpCanister.query_blocks, {
args: [getBlocksArgs]
});
}
}
),
getSymbol: update([], text, async () => {
const symbolResult = await ic.call(icpCanister.symbol, {});
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/symbol`,
{
body: serialize({
candidPath: `/src/ledger.did`
})
}
);

return symbolResult.symbol;
return (await response.json()).symbol;
} else {
const symbolResult = await ic.call(icpCanister.symbol);

return symbolResult.symbol;
}
}),
getName: update([], text, async () => {
const nameResult = await ic.call(icpCanister.name, {});
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/name`,
{
body: serialize({
candidPath: `/src/ledger.did`
})
}
);

return (await response.json()).name;
} else {
const nameResult = await ic.call(icpCanister.name);

return nameResult.name;
return nameResult.name;
}
}),
getDecimals: update([], nat32, async () => {
const decimalsResult = await ic.call(icpCanister.decimals, {});
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/decimals`,
{
body: serialize({
candidPath: `/src/ledger.did`
})
}
);

return (await response.json()).decimals;
} else {
const decimalsResult = await ic.call(icpCanister.decimals);

return decimalsResult.decimals;
return decimalsResult.decimals;
}
}),
getArchives: update([], Archives, async () => {
return await ic.call(icpCanister.archives, {});
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(
`icp://${getIcpCanisterPrincipal()}/archives`,
{
body: serialize({
candidPath: `/src/ledger.did`
})
}
);

return await response.json();
} else {
return await ic.call(icpCanister.archives);
}
}),
getAddressFromPrincipal: query([Principal], text, (principal) => {
return hexAddressFromPrincipal(principal, 0);
})
});

function agentOptToAzleOpt<T>(opt: [T] | []): Opt<T> {
if (opt.length === 0) {
return None;
} else {
return Some(opt[0]);
}
}

function getIcpCanisterPrincipal(): string {
if (process.env.ICP_CANISTER_PRINCIPAL !== undefined) {
return process.env.ICP_CANISTER_PRINCIPAL;
} else {
throw new Error('process.env.ICP_CANISTER_PRINCIPAL is undefined');
}
}

0 comments on commit 5d411eb

Please sign in to comment.