Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getOrPromptFilePath,
getOrPromptDeployEnv,
validateAndExecute,
getInteractiveInput,
getInteractiveSelect,
} from "@internal/utils/cli";
import { argsSchema, handleWrapToken } from "./wrap-token.handler";
Expand All @@ -17,6 +18,7 @@ type CommanderOptions = {
decimals?: string;
name?: string;
symbol?: string;
uri?: string;
remoteToken?: string;
scalerExponent?: string;
payerKp?: string;
Expand Down Expand Up @@ -50,6 +52,19 @@ async function collectInteractiveOptions(
"wERC20"
);

if (opts.uri === undefined) {
opts.uri = await getInteractiveInput(
"Enter token metadata uri, which serves the icon and description (leave blank for none)",
"https://example.com/token.json",
(input) => {
const result = argsSchema.shape.uri.safeParse(input.trim());
return result.success
? undefined
: result.error.issues[0]?.message || "Invalid uri";
}
);
}

if (!opts.remoteToken) {
const remoteToken = await getInteractiveSelect({
message: "Select remote token:",
Expand Down Expand Up @@ -102,6 +117,10 @@ export const wrapTokenCommand = new Command("wrap-token")
.option("--decimals <decimals>", "Token decimals")
.option("--name <name>", "Token name")
.option("--symbol <symbol>", "Token symbol")
.option(
"--uri <uri>",
"Uri of the off-chain JSON metadata serving the token icon and description"
)
.option(
"--remote-token <remoteToken>",
"Remote token address: 'constant-erc20', 'constant-eth', or custom address"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { keccak256, toBytes } from "viem";

import {
fetchBridge,
getWrapTokenInstruction,
type WrapTokenInstructionDataArgs,
getWrapTokenV2Instruction,
type WrapTokenV2InstructionDataArgs,
} from "@base/bridge/bridge";

import { logger } from "@internal/logger";
Expand Down Expand Up @@ -49,6 +49,16 @@ export const argsSchema = z.object({
.nonempty("Token name cannot be empty")
.default("Wrapped ERC20"),
symbol: z.string().nonempty("Token symbol cannot be empty").default("wERC20"),
uri: z
.string()
.refine((val) => val.trim() === "" || URL.canParse(val.trim()), {
message: "Token uri must be a valid URL",
})
.refine(
(val) => Buffer.byteLength(val, "utf8") <= getIdlConstant("MAX_URI_LEN"),
{ message: "Token uri is too long" }
)
.default(""),
remoteToken: z.union([
z.literal("constant-erc20"),
z.literal("constant-eth"),
Expand Down Expand Up @@ -98,11 +108,12 @@ export async function handleWrapToken(args: Args): Promise<void> {
logger.info(`Outgoing message: ${outgoingMessage}`);

// Instruction arguments
const instructionArgs: WrapTokenInstructionDataArgs = {
const instructionArgs: WrapTokenV2InstructionDataArgs = {
outgoingMessageSalt: salt,
decimals: args.decimals,
name: args.name,
symbol: args.symbol,
uri: args.uri,
remoteToken: toBytes(remoteToken),
scalerExponent: args.scalerExponent,
};
Expand All @@ -115,7 +126,9 @@ export async function handleWrapToken(args: Args): Promise<void> {
instructionArgs.symbol.length
);

// Calculate metadata hash
// Calculate metadata hash.
// NOTE: `uri` is deliberately absent, mirroring `PartialTokenMetadata::hash` onchain. Adding it
// here would derive a mint the program does not recognize.
const metadataHash = keccak256(
Buffer.concat([
Buffer.from(nameLengthLeBytes),
Expand Down Expand Up @@ -148,7 +161,7 @@ export async function handleWrapToken(args: Args): Promise<void> {

// Build wrap token instruction
const ixs: Instruction[] = [
getWrapTokenInstruction(
getWrapTokenV2Instruction(
{
// Accounts
payer,
Expand Down