Skip to content

Commit 7368255

Browse files
feat(docs): updated custom instruction typescript guide
1 parent a93ac1d commit 7368255

File tree

2 files changed

+129
-16
lines changed

2 files changed

+129
-16
lines changed

docs/smart-accounts/guides/typescript-viem/03-custom-instruction.mdx

Lines changed: 115 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ As described in the general [Custom Instruction guide](/smart-accounts/custom-in
164164
Behind the scenes, each custom instruction call translates to sending a payment transaction to the `targetContract` address of `value` FLR with attached `data`.
165165
:::
166166

167+
We define the `CustomInstruction` type:
168+
169+
```typescript
170+
export type CustomInstruction = {
171+
targetContract: Address;
172+
value: bigint;
173+
data: `0x${string}`;
174+
};
175+
```
176+
167177
The `targetContract` and `value` do not need additional processing, but how can we generate the calldata?
168178
For that, we will use the `encodeFunctionData` function from the Viem library.
169179
It calculates the encoding for a function from its contract's ABI, the function name, and the arguments used.
@@ -236,7 +246,6 @@ Then we call the `writeContract` Viem function, and actually register the instru
236246

237247
If the instruction has already been registered, a `CustomInstructionAlreadyRegistered` event is emitted.
238248
Otherwise, the instruction is registered, and a `CustomInstructionRegistered` event is emitted instead.
239-
In either case, the 32-byte encoding of the custom instruction, called the `callHash`, is returned.
240249

241250
```typescript
242251
export async function registerCustomInstruction(
@@ -251,35 +260,43 @@ export async function registerCustomInstruction(
251260
});
252261
console.log("request:", request, "\n");
253262

254-
const registerCustomInstructionResult =
263+
const registerCustomInstructionTransaction =
255264
await walletClient.writeContract(request);
256265
console.log(
257-
"registerCustomInstructionResult:",
258-
registerCustomInstructionResult,
266+
"Register custom instruction transaction:",
267+
registerCustomInstructionTransaction,
259268
"\n",
260269
);
261270

262-
return registerCustomInstructionResult;
271+
return registerCustomInstructionTransaction;
263272
}
264273
```
265274

266275
## Encoding an instruction
267276

268277
Before the custom instruction can be sent on the XRPL, it must be properly encoded.
269-
The 32-byte `callHash` that the `registerCustomInstruction` function returns is not yet a proper instruction encoding.
278+
We call the `encodeCustomInstruction` read function of the `MasterAccountController` contract with the instruction as parameter.
279+
It returns a 32-byte `callHash`, which is not yet a proper instruction encoding.
270280
We need to replace the first two bytes with the following values:
271281

272282
- 1st byte: custom instruction command ID `0xff`
273283
- 2nd byte: wallet identifier `walletId` described above
274284

275285
```typescript
276-
export function encodeCustomInstruction(
277-
instructionHash: `0x${string}`,
286+
async function encodeCustomInstruction(
287+
instructions: CustomInstruction[],
278288
walletId: number,
279289
) {
290+
const encodedInstruction = (await publicClient.readContract({
291+
address: MASTER_ACCOUNT_CONTROLLER_ADDRESS,
292+
abi: abi,
293+
functionName: "encodeCustomInstruction",
294+
args: [instructions],
295+
})) as `0x${string}`;
296+
// NOTE:(Nik) We cut off the `0x` prefix and the first 2 bytes to get the length down to 30 bytes
280297
return ("0xff" +
281298
toHex(walletId, { size: 1 }).slice(2) +
282-
instructionHash.slice(6)) as `0x${string}`;
299+
encodedInstruction.slice(6)) as `0x${string}`;
283300
}
284301
```
285302

@@ -411,7 +428,7 @@ async function waitForCustomInstructionExecutedEvent({
411428
}
412429
```
413430

414-
The vehicle for bridging the instruction is FDC, which puts a maximum cap of 180 seconds on the process duration.
431+
The vehicle for bridging the instruction is FDC, which puts a maximum cap of 180 seconds on the process duration.
415432

416433
## Full script
417434

@@ -420,3 +437,91 @@ The repository with the above example is available on [GitHub](https://github.co
420437
<CodeBlock language="typescript" title="src/custom-instructions.ts">
421438
{CustomInstructionsScript}
422439
</CodeBlock>
440+
441+
## Expected output
442+
443+
```bash
444+
Custom instructions: [
445+
{
446+
targetContract: '0xEE6D54382aA623f4D16e856193f5f8384E487002',
447+
value: 0n,
448+
data: '0x80abd133'
449+
},
450+
{
451+
targetContract: '0x42Ccd4F0aB1C6Fa36BfA37C9e30c4DC4DD94dE42',
452+
value: 1000000000000000000n,
453+
data: '0xd0e30db0'
454+
},
455+
{
456+
targetContract: '0x59D57652BF4F6d97a6e555800b3920Bd775661Dc',
457+
value: 1000000000000000000n,
458+
data: '0x28d106b20000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f20576f726c64210000000000000000000000000000000000000000'
459+
}
460+
]
461+
462+
Personal account address: 0xFd2f0eb6b9fA4FE5bb1F7B26fEE3c647ed103d9F
463+
464+
request: {
465+
abi: [
466+
{
467+
inputs: [Array],
468+
name: 'registerCustomInstruction',
469+
outputs: [Array],
470+
stateMutability: 'nonpayable',
471+
type: 'function'
472+
}
473+
],
474+
address: '0x434936d47503353f06750Db1A444DBDC5F0AD37c',
475+
args: [ [ [Object], [Object], [Object] ] ],
476+
dataSuffix: undefined,
477+
functionName: 'registerCustomInstruction',
478+
account: {
479+
address: '0xF5488132432118596fa13800B68df4C0fF25131d',
480+
nonceManager: undefined,
481+
sign: [AsyncFunction: sign],
482+
signAuthorization: [AsyncFunction: signAuthorization],
483+
signMessage: [AsyncFunction: signMessage],
484+
signTransaction: [AsyncFunction: signTransaction],
485+
signTypedData: [AsyncFunction: signTypedData],
486+
source: 'privateKey',
487+
type: 'local',
488+
publicKey: '0x04cd84974fb965b048aa4230fdf0a2735c951f28b076727e1067bf56577a59b1cbc41e8d03b33acc9105828f048c0cf940c78bba518b0da2765078ca2a42e056c3'
489+
}
490+
}
491+
492+
Register custom instruction transaction: 0x2f6500791d89ea7f8ff5f3ce8983d92d7aaf62e04207513e8e0b2a2bfdf09a1e
493+
494+
Custom instruction call hash: 0x2f6500791d89ea7f8ff5f3ce8983d92d7aaf62e04207513e8e0b2a2bfdf09a1e
495+
496+
Encoded instructions: 0xff00e54bf1b4e93c5306e08d919864ec111211c4fb36960261e8018da9959791
497+
498+
instructionIdDecimal: 255n
499+
500+
Instruction fee: 0.001
501+
502+
Custom instruction transaction hash: 03165B82E4B7BB168AF7B217C9EC833896E968C796CA8BF8D2E66879ED311909
503+
504+
Waiting for CustomInstructionExecuted event...
505+
CustomInstructionExecuted event: {
506+
eventName: 'CustomInstructionExecuted',
507+
args: {
508+
personalAccount: '0xFd2f0eb6b9fA4FE5bb1F7B26fEE3c647ed103d9F',
509+
callHash: '0x0000e54bf1b4e93c5306e08d919864ec111211c4fb36960261e8018da9959791',
510+
customInstruction: [ [Object], [Object], [Object] ]
511+
},
512+
address: '0x434936d47503353f06750db1a444dbdc5f0ad37c',
513+
topics: [
514+
'0x1c09418c54894f576841186935c5f666b3bedd66c29f3a03bcab4051fe2509f3',
515+
'0x000000000000000000000000fd2f0eb6b9fa4fe5bb1f7b26fee3c647ed103d9f',
516+
'0x0000e54bf1b4e93c5306e08d919864ec111211c4fb36960261e8018da9959791'
517+
],
518+
data: '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000ee6d54382aa623f4d16e856193f5f8384e48700200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000480abd1330000000000000000000000000000000000000000000000000000000000000000000000000000000042ccd4f0ab1c6fa36bfa37c9e30c4dc4dd94de420000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004d0e30db00000000000000000000000000000000000000000000000000000000000000000000000000000000059d57652bf4f6d97a6e555800b3920bd775661dc0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006428d106b20000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
519+
blockNumber: 26663892n,
520+
transactionHash: '0xe3ba1751d63a9b11ff3b3185254c28c447f1db6ff101708f6968ff935c28e3f3',
521+
transactionIndex: 2,
522+
blockHash: '0x69a3b948a56a5195fbe0ced46b157ca33390226a4503f59a3d8e59fe07538d23',
523+
logIndex: 7,
524+
removed: false,
525+
blockTimestamp: undefined
526+
}
527+
```

examples/developer-hub-javascript/smart-accounts/custom-instructions.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,22 @@ import { publicClient } from "./utils/client";
1515
import { sendXrplPayment } from "./utils/xrpl";
1616
import { Client, Wallet } from "xrpl";
1717
import type { CustomInstructionExecutedEventType } from "./utils/event-types";
18+
import { abi } from "./abis/CustomInstructionsFacet";
1819

19-
export function encodeCustomInstruction(
20-
instructionHash: `0x${string}`,
20+
async function encodeCustomInstruction(
21+
instructions: CustomInstruction[],
2122
walletId: number,
2223
) {
24+
const encodedInstruction = (await publicClient.readContract({
25+
address: MASTER_ACCOUNT_CONTROLLER_ADDRESS,
26+
abi: abi,
27+
functionName: "encodeCustomInstruction",
28+
args: [instructions],
29+
})) as `0x${string}`;
2330
// NOTE:(Nik) We cut off the `0x` prefix and the first 2 bytes to get the length down to 30 bytes
2431
return ("0xff" +
2532
toHex(walletId, { size: 1 }).slice(2) +
26-
instructionHash.slice(6)) as `0x${string}`;
33+
encodedInstruction.slice(6)) as `0x${string}`;
2734
}
2835

2936
async function sendCustomInstruction({
@@ -145,10 +152,11 @@ async function main() {
145152
);
146153
console.log("Personal account address:", personalAccountAddress, "\n");
147154

148-
const customInstructionHash =
155+
const customInstructionCallHash =
149156
await registerCustomInstruction(customInstructions);
150-
const encodedInstruction = encodeCustomInstruction(
151-
customInstructionHash,
157+
console.log("Custom instruction call hash:", customInstructionCallHash, "\n");
158+
const encodedInstruction = await encodeCustomInstruction(
159+
customInstructions,
152160
walletId,
153161
);
154162
console.log("Encoded instructions:", encodedInstruction, "\n");

0 commit comments

Comments
 (0)