Skip to content

Commit bc34020

Browse files
marc0oloclaude
andauthored
chore: migrate motoko/icp_transfer to icp-cli (#1389)
* chore: migrate motoko/icp_transfer to icp-cli - Replace dfx.json with icp.yaml using @dfinity/motoko@v5.0.0 recipe - Move src/icp_transfer_backend/main.mo → backend/app.mo - Refactor Motoko code: replace canister import with direct actor reference to the ICP ledger at its well-known principal ryjl3-tyaaa-aaaaa-aaaba-cai (available on both mainnet and the icp-cli local network's NNS subnet) - Update mops.toml: moc 1.9.0, core 2.5.0, --default-persistent-actors flag - Add Makefile with test target (funds backend then tests transfer function) - Add CI workflow .github/workflows/icp_transfer.yml (motoko-only job) - Delete dfx.json, demo.sh, src/ directory, old per-example .gitignore - Delete old .github/workflows/motoko-icp-transfer-example.yml - Add mops.lock to root .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(icp_transfer): remove old Motoko workflow and Candid section from README Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(icp_transfer): remove GitHub link and Overview heading from README Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(icp_transfer): fix Makefile grep, -i flag, and clarify created_at_time comment - grep -q '#ok' → grep -q 'ok': Candid output is (variant { ok = N : nat64 }), no # prefix - --id-only → -i: matches the canonical flag used across all Makefiles - Clarify created_at_time = null comment: null stores current IC time in the block but also disables deduplication protection Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * improve(icp_transfer): add balance checks via icrc1_balance_of Tests now verify the actual ICP balance deltas on the ledger: - Test 1: fund backend with 2 ICP, assert backend gained +200_000_000 e8s - Test 2: transfer 1 ICP, assert backend lost -100_010_000 e8s (amount + fee) and recipient gained +100_000_000 e8s exactly Delta-based checks so tests are idempotent across re-runs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refocus(icp_transfer): highlight toLedgerAccount with dual transfer paths - toAccountId(principal, subaccount) — query that surfaces the AccountIdentifier blob so callers can inspect the conversion - transferToPrincipal — converts internally via toLedgerAccount - transferToAccountId — accepts the blob directly (exchange use case) - make test calls both with the same recipient and verifies identical balance deltas, proving the two paths reach the same account - README rewritten around AccountIdentifier concept; removes broken token_transfer reference; notes ICRC-1 alternative Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(icp_transfer): clarify CEX vs wallet account format in README CEXs use AccountIdentifier; wallets prefer ICRC-1 account format. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(icp_transfer): use icp token balance + shell function for robust balance checks - Define get_balance() shell function at the top of the test block to avoid repeating the awk conversion; avoids Make/shell variable expansion traps with $(call ...) and $$-style variables - icp token balance -q outputs ICP as a decimal; awk converts to e8s integer for exact arithmetic — avoids the underscore-in-Candid-nat parsing bug where grep -oE '[0-9]+' on (400_000_000 : nat) gave 400 not 400000000 - Delta-based checks remain idempotent across re-runs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refocus(icp_transfer): AccountIdentifier, fee-aware tests, hex mops library Backend (3 public functions): - Remove toAccountId (raw blob, superseded by toAccountIdHex) - toAccountIdHex: use hex mops library instead of manual nibble arithmetic - transferToPrincipal / transferToAccountId: unchanged code paths Makefile (3 tests): - Test 1: assert icp identity account-id --format ledger == toAccountIdHex - Test 2: transferToPrincipal — 99_990_000 e8s + 10_000 fee = exactly 1 ICP - Test 3: transferToAccountId — AccountIdentifier as vec { N : nat8; ... } constructed from hex via fold -w2 and bash $((16#xx)) arithmetic; blob "\xx..." fails with non-ASCII bytes in @dfinity/candid (reproduces on icp-cli 0.3.2 and 1.0.0 — likely a bug in the Candid text parser) - Fund 2 ICP, both transfers drain it to zero (highlights fee deduction) CI: bump icp-dev-env-motoko 0.3.2 → 1.0.0 mops.toml: add hex = "1.0.3" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(icp_transfer): use printf '%d' 0x for POSIX sh hex conversion $((16#xx)) is bash-only. CI runs Makefile recipes with /bin/sh (dash) which doesn't support this syntax, producing an empty vec. printf '%d' "0x$b" is POSIX-specified and works in both dash and bash. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bafb2cf commit bc34020

12 files changed

Lines changed: 221 additions & 469 deletions

File tree

.github/workflows/icp_transfer.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: icp_transfer
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
paths:
8+
- motoko/icp_transfer/**
9+
- .github/workflows/icp_transfer.yml
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
motoko-icp_transfer:
17+
runs-on: ubuntu-24.04
18+
container: ghcr.io/dfinity/icp-dev-env-motoko:1.0.0
19+
env:
20+
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
steps:
22+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
23+
- name: Deploy and test
24+
working-directory: motoko/icp_transfer
25+
run: |
26+
icp network start -d
27+
icp deploy
28+
make test

.github/workflows/motoko-icp-transfer-example.yml

Lines changed: 0 additions & 39 deletions
This file was deleted.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ target/
1212
*.old.did
1313
.idea
1414
.mops
15+
mops.lock
1516
.env
1617
**/frontend/src/bindings/
1718
**/src/declarations/

motoko/icp_transfer/.gitignore

Lines changed: 0 additions & 25 deletions
This file was deleted.

motoko/icp_transfer/Makefile

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
.PHONY: test
2+
3+
# Tests use a single shell block so all variables and functions stay in scope.
4+
# Balance checks are delta-based so the tests are idempotent across re-runs.
5+
#
6+
# Transfer amount is 99_990_000 e8s so that amount + fee (10_000) = 100_000_000 e8s
7+
# = exactly 1 ICP deducted per transfer. Two tests drain the 2 ICP funding to zero.
8+
test:
9+
@set -e; \
10+
\
11+
backend=$$(icp canister status backend -i); \
12+
recipient=$$(icp identity principal); \
13+
echo "Backend: $$backend"; \
14+
echo "Recipient: $$recipient"; \
15+
\
16+
get_balance() { \
17+
icp token balance -q --of-principal "$$1" \
18+
| awk '{printf "%.0f", $$1 * 100000000}'; \
19+
}; \
20+
\
21+
echo "=== Test 1: toAccountIdHex matches icp identity account-id ==="; \
22+
cli_hex=$$(icp identity account-id --format ledger); \
23+
backend_hex=$$(icp canister call --query backend toAccountIdHex \
24+
"(principal \"$$recipient\", null)" | grep -oE '[0-9a-f]{64}'); \
25+
echo " icp identity account-id: $$cli_hex"; \
26+
echo " backend toAccountIdHex: $$backend_hex"; \
27+
[ "$$cli_hex" = "$$backend_hex" ] || (echo "FAIL: hex values differ" && exit 1); \
28+
echo " PASS"; \
29+
\
30+
echo "=== Setup: fund backend with 2 ICP ==="; \
31+
icp token transfer 2 "$$backend"; \
32+
\
33+
echo "=== Test 2: transferToPrincipal — deducts exactly 1 ICP (99_990_000 amount + 10_000 fee) ==="; \
34+
backend_before=$$(get_balance "$$backend"); \
35+
recipient_before=$$(get_balance "$$recipient"); \
36+
result=$$(icp canister call backend transferToPrincipal \
37+
"(record { e8s = 99_990_000 : nat64 }, principal \"$$recipient\", null)"); \
38+
echo " result: $$result"; \
39+
echo "$$result" | grep -q 'ok' || (echo "FAIL: transfer rejected" && exit 1); \
40+
backend_after=$$(get_balance "$$backend"); \
41+
recipient_after=$$(get_balance "$$recipient"); \
42+
echo " backend: $$backend_before → $$backend_after (delta $$((backend_after - backend_before)))"; \
43+
echo " recipient: $$recipient_before → $$recipient_after (delta $$((recipient_after - recipient_before)))"; \
44+
[ "$$((backend_after - backend_before))" -eq -100000000 ] || (echo "FAIL: expected backend delta -100000000 e8s" && exit 1); \
45+
[ "$$((recipient_after - recipient_before))" -eq 99990000 ] || (echo "FAIL: expected recipient delta +99990000 e8s" && exit 1); \
46+
echo " PASS"; \
47+
\
48+
echo "=== Test 3: transferToAccountId — same recipient, same deduction ==="; \
49+
account_id_vec="vec { $$(echo "$$cli_hex" | fold -w2 | while read b; do printf '%d : nat8; ' "0x$$b"; done | sed 's/; $$//') }"; \
50+
echo " AccountIdentifier: $$account_id_vec"; \
51+
backend_before=$$(get_balance "$$backend"); \
52+
recipient_before=$$(get_balance "$$recipient"); \
53+
result=$$(icp canister call backend transferToAccountId \
54+
"(record { e8s = 99_990_000 : nat64 }, $$account_id_vec)"); \
55+
echo " result: $$result"; \
56+
echo "$$result" | grep -q 'ok' || (echo "FAIL: transfer rejected" && exit 1); \
57+
backend_after=$$(get_balance "$$backend"); \
58+
recipient_after=$$(get_balance "$$recipient"); \
59+
echo " backend: $$backend_before → $$backend_after (delta $$((backend_after - backend_before)))"; \
60+
echo " recipient: $$recipient_before → $$recipient_after (delta $$((recipient_after - recipient_before)))"; \
61+
[ "$$((backend_after - backend_before))" -eq -100000000 ] || (echo "FAIL: expected backend delta -100000000 e8s" && exit 1); \
62+
[ "$$((recipient_after - recipient_before))" -eq 99990000 ] || (echo "FAIL: expected recipient delta +99990000 e8s" && exit 1); \
63+
echo " PASS — same recipient reached via AccountIdentifier blob as via principal"

0 commit comments

Comments
 (0)