solana: fix token metadata program interaction (#2913)
* testing: fix pubkey caused by 0b0b9cea70
* solana: fix token-metadata forked dependency
* sdk/js: fix tokenMetadata and Solana IDLs
* testing: fix tests; add token-metadata dependency
---------
Co-authored-by: A5 Pickle <a5-pickle@users.noreply.github.com>
This commit is contained in:
parent
3a28b6169a
commit
6f8c8430ac
|
@ -177,7 +177,7 @@ jobs:
|
||||||
export PATH="${HOME}/.local/share/solana/install/active_release/bin:${PATH}"
|
export PATH="${HOME}/.local/share/solana/install/active_release/bin:${PATH}"
|
||||||
|
|
||||||
mkdir -p "${BPF_OUT_DIR}"
|
mkdir -p "${BPF_OUT_DIR}"
|
||||||
cp modules/token_bridge/token-metadata/spl_token_metadata.so "${BPF_OUT_DIR}"
|
cp external/mpl_token_metadata.so "${BPF_OUT_DIR}"
|
||||||
|
|
||||||
BPF_PACKAGES=(
|
BPF_PACKAGES=(
|
||||||
bridge/program/Cargo.toml
|
bridge/program/Cargo.toml
|
||||||
|
|
|
@ -52,7 +52,7 @@ spec:
|
||||||
- /opt/solana/deps/cpi_poster.so
|
- /opt/solana/deps/cpi_poster.so
|
||||||
- --bpf-program
|
- --bpf-program
|
||||||
- metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s
|
- metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s
|
||||||
- /opt/solana/deps/spl_token_metadata.so
|
- /opt/solana/deps/mpl_token_metadata.so
|
||||||
- --bpf-program
|
- --bpf-program
|
||||||
- Ex9bCdVMSfx7EzB3pgSi2R4UHwJAXvTw18rBQm5YQ8gK
|
- Ex9bCdVMSfx7EzB3pgSi2R4UHwJAXvTw18rBQm5YQ8gK
|
||||||
- /opt/solana/deps/wormhole_migration.so
|
- /opt/solana/deps/wormhole_migration.so
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import {
|
||||||
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
} from "@solana/spl-token";
|
||||||
import {
|
import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
PublicKeyInitData,
|
PublicKeyInitData,
|
||||||
|
@ -5,26 +9,22 @@ import {
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import {
|
|
||||||
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
||||||
TOKEN_PROGRAM_ID,
|
|
||||||
} from "@solana/spl-token";
|
|
||||||
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
|
||||||
import { deriveClaimKey, derivePostedVaaKey } from "../../wormhole";
|
|
||||||
import {
|
|
||||||
deriveEndpointKey,
|
|
||||||
deriveNftBridgeConfigKey,
|
|
||||||
deriveWrappedMintKey,
|
|
||||||
deriveWrappedMetaKey,
|
|
||||||
deriveMintAuthorityKey,
|
|
||||||
} from "../accounts";
|
|
||||||
import {
|
import {
|
||||||
isBytes,
|
isBytes,
|
||||||
ParsedNftTransferVaa,
|
ParsedNftTransferVaa,
|
||||||
parseNftTransferVaa,
|
parseNftTransferVaa,
|
||||||
SignedVaa,
|
SignedVaa,
|
||||||
} from "../../../vaa";
|
} from "../../../vaa";
|
||||||
import { SplTokenMetadataProgram } from "../../utils";
|
import { TOKEN_METADATA_PROGRAM_ID } from "../../utils";
|
||||||
|
import { deriveClaimKey, derivePostedVaaKey } from "../../wormhole";
|
||||||
|
import {
|
||||||
|
deriveEndpointKey,
|
||||||
|
deriveMintAuthorityKey,
|
||||||
|
deriveNftBridgeConfigKey,
|
||||||
|
deriveWrappedMetaKey,
|
||||||
|
deriveWrappedMintKey,
|
||||||
|
} from "../accounts";
|
||||||
|
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
||||||
|
|
||||||
export function createCompleteTransferWrappedInstruction(
|
export function createCompleteTransferWrappedInstruction(
|
||||||
nftBridgeProgramId: PublicKeyInitData,
|
nftBridgeProgramId: PublicKeyInitData,
|
||||||
|
@ -110,7 +110,7 @@ export function getCompleteTransferWrappedAccounts(
|
||||||
rent: SYSVAR_RENT_PUBKEY,
|
rent: SYSVAR_RENT_PUBKEY,
|
||||||
systemProgram: SystemProgram.programId,
|
systemProgram: SystemProgram.programId,
|
||||||
tokenProgram: TOKEN_PROGRAM_ID,
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
splMetadataProgram: SplTokenMetadataProgram.programId,
|
splMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
|
||||||
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
wormholeProgram: new PublicKey(wormholeProgramId),
|
wormholeProgram: new PublicKey(wormholeProgramId),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import {
|
import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
PublicKeyInitData,
|
PublicKeyInitData,
|
||||||
|
@ -5,26 +6,22 @@ import {
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
||||||
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
|
||||||
import { derivePostedVaaKey } from "../../wormhole";
|
|
||||||
import {
|
|
||||||
deriveEndpointKey,
|
|
||||||
deriveNftBridgeConfigKey,
|
|
||||||
deriveWrappedMintKey,
|
|
||||||
deriveWrappedMetaKey,
|
|
||||||
deriveMintAuthorityKey,
|
|
||||||
} from "../accounts";
|
|
||||||
import {
|
import {
|
||||||
isBytes,
|
isBytes,
|
||||||
ParsedNftTransferVaa,
|
ParsedNftTransferVaa,
|
||||||
parseNftTransferVaa,
|
parseNftTransferVaa,
|
||||||
SignedVaa,
|
SignedVaa,
|
||||||
} from "../../../vaa";
|
} from "../../../vaa";
|
||||||
|
import { deriveTokenMetadataKey, TOKEN_METADATA_PROGRAM_ID } from "../../utils";
|
||||||
|
import { derivePostedVaaKey } from "../../wormhole";
|
||||||
import {
|
import {
|
||||||
deriveSplTokenMetadataKey,
|
deriveEndpointKey,
|
||||||
SplTokenMetadataProgram,
|
deriveMintAuthorityKey,
|
||||||
} from "../../utils";
|
deriveNftBridgeConfigKey,
|
||||||
|
deriveWrappedMetaKey,
|
||||||
|
deriveWrappedMintKey,
|
||||||
|
} from "../accounts";
|
||||||
|
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
||||||
|
|
||||||
export function createCompleteWrappedMetaInstruction(
|
export function createCompleteWrappedMetaInstruction(
|
||||||
nftBridgeProgramId: PublicKeyInitData,
|
nftBridgeProgramId: PublicKeyInitData,
|
||||||
|
@ -92,12 +89,12 @@ export function getCompleteWrappedMetaAccounts(
|
||||||
),
|
),
|
||||||
mint,
|
mint,
|
||||||
wrappedMeta: deriveWrappedMetaKey(nftBridgeProgramId, mint),
|
wrappedMeta: deriveWrappedMetaKey(nftBridgeProgramId, mint),
|
||||||
splMetadata: deriveSplTokenMetadataKey(mint),
|
splMetadata: deriveTokenMetadataKey(mint),
|
||||||
mintAuthority: deriveMintAuthorityKey(nftBridgeProgramId),
|
mintAuthority: deriveMintAuthorityKey(nftBridgeProgramId),
|
||||||
rent: SYSVAR_RENT_PUBKEY,
|
rent: SYSVAR_RENT_PUBKEY,
|
||||||
systemProgram: SystemProgram.programId,
|
systemProgram: SystemProgram.programId,
|
||||||
tokenProgram: TOKEN_PROGRAM_ID,
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
splMetadataProgram: SplTokenMetadataProgram.programId,
|
splMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
|
||||||
wormholeProgram: new PublicKey(wormholeProgramId),
|
wormholeProgram: new PublicKey(wormholeProgramId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
|
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import {
|
import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
PublicKeyInitData,
|
PublicKeyInitData,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
import { TOKEN_METADATA_PROGRAM_ID, deriveTokenMetadataKey } from "../../utils";
|
||||||
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
|
||||||
import { getPostMessageAccounts } from "../../wormhole";
|
import { getPostMessageAccounts } from "../../wormhole";
|
||||||
import {
|
import {
|
||||||
deriveAuthoritySignerKey,
|
deriveAuthoritySignerKey,
|
||||||
|
deriveCustodyKey,
|
||||||
deriveCustodySignerKey,
|
deriveCustodySignerKey,
|
||||||
deriveNftBridgeConfigKey,
|
deriveNftBridgeConfigKey,
|
||||||
deriveCustodyKey,
|
|
||||||
} from "../accounts";
|
} from "../accounts";
|
||||||
import {
|
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
||||||
deriveSplTokenMetadataKey,
|
|
||||||
SplTokenMetadataProgram,
|
|
||||||
} from "../../utils";
|
|
||||||
|
|
||||||
export function createTransferNativeInstruction(
|
export function createTransferNativeInstruction(
|
||||||
nftBridgeProgramId: PublicKeyInitData,
|
nftBridgeProgramId: PublicKeyInitData,
|
||||||
|
@ -103,7 +100,7 @@ export function getTransferNativeAccounts(
|
||||||
config: deriveNftBridgeConfigKey(nftBridgeProgramId),
|
config: deriveNftBridgeConfigKey(nftBridgeProgramId),
|
||||||
from: new PublicKey(from),
|
from: new PublicKey(from),
|
||||||
mint: new PublicKey(mint),
|
mint: new PublicKey(mint),
|
||||||
splMetadata: deriveSplTokenMetadataKey(mint),
|
splMetadata: deriveTokenMetadataKey(mint),
|
||||||
custody: deriveCustodyKey(nftBridgeProgramId, mint),
|
custody: deriveCustodyKey(nftBridgeProgramId, mint),
|
||||||
authoritySigner: deriveAuthoritySignerKey(nftBridgeProgramId),
|
authoritySigner: deriveAuthoritySignerKey(nftBridgeProgramId),
|
||||||
custodySigner: deriveCustodySignerKey(nftBridgeProgramId),
|
custodySigner: deriveCustodySignerKey(nftBridgeProgramId),
|
||||||
|
@ -116,7 +113,7 @@ export function getTransferNativeAccounts(
|
||||||
rent,
|
rent,
|
||||||
systemProgram,
|
systemProgram,
|
||||||
tokenProgram: TOKEN_PROGRAM_ID,
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
splMetadataProgram: SplTokenMetadataProgram.programId,
|
splMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
|
||||||
wormholeProgram: new PublicKey(wormholeProgramId),
|
wormholeProgram: new PublicKey(wormholeProgramId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import {
|
import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
PublicKeyInitData,
|
PublicKeyInitData,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
import { TOKEN_METADATA_PROGRAM_ID, deriveTokenMetadataKey } from "../../utils";
|
||||||
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
|
||||||
import { getPostMessageAccounts } from "../../wormhole";
|
import { getPostMessageAccounts } from "../../wormhole";
|
||||||
import {
|
import {
|
||||||
deriveAuthoritySignerKey,
|
deriveAuthoritySignerKey,
|
||||||
|
@ -12,10 +12,7 @@ import {
|
||||||
deriveWrappedMetaKey,
|
deriveWrappedMetaKey,
|
||||||
deriveWrappedMintKey,
|
deriveWrappedMintKey,
|
||||||
} from "../accounts";
|
} from "../accounts";
|
||||||
import {
|
import { createReadOnlyNftBridgeProgramInterface } from "../program";
|
||||||
deriveSplTokenMetadataKey,
|
|
||||||
SplTokenMetadataProgram,
|
|
||||||
} from "../../utils";
|
|
||||||
|
|
||||||
export function createTransferWrappedInstruction(
|
export function createTransferWrappedInstruction(
|
||||||
nftBridgeProgramId: PublicKeyInitData,
|
nftBridgeProgramId: PublicKeyInitData,
|
||||||
|
@ -120,7 +117,7 @@ export function getTransferWrappedAccounts(
|
||||||
fromOwner: new PublicKey(fromOwner),
|
fromOwner: new PublicKey(fromOwner),
|
||||||
mint,
|
mint,
|
||||||
wrappedMeta: deriveWrappedMetaKey(nftBridgeProgramId, mint),
|
wrappedMeta: deriveWrappedMetaKey(nftBridgeProgramId, mint),
|
||||||
splMetadata: deriveSplTokenMetadataKey(mint),
|
splMetadata: deriveTokenMetadataKey(mint),
|
||||||
authoritySigner: deriveAuthoritySignerKey(nftBridgeProgramId),
|
authoritySigner: deriveAuthoritySignerKey(nftBridgeProgramId),
|
||||||
wormholeBridge,
|
wormholeBridge,
|
||||||
wormholeMessage,
|
wormholeMessage,
|
||||||
|
@ -131,7 +128,7 @@ export function getTransferWrappedAccounts(
|
||||||
rent,
|
rent,
|
||||||
systemProgram,
|
systemProgram,
|
||||||
tokenProgram: TOKEN_PROGRAM_ID,
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
splMetadataProgram: SplTokenMetadataProgram.programId,
|
splMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
|
||||||
wormholeProgram: new PublicKey(wormholeProgramId),
|
wormholeProgram: new PublicKey(wormholeProgramId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from "../../../utils";
|
} from "../../../utils";
|
||||||
import { deriveAddress, getAccountData } from "../../utils";
|
import { deriveAddress, getAccountData } from "../../utils";
|
||||||
|
|
||||||
export { deriveSplTokenMetadataKey } from "../../utils/splMetadata";
|
export { deriveTokenMetadataKey } from "../../utils/tokenMetadata";
|
||||||
|
|
||||||
export function deriveWrappedMintKey(
|
export function deriveWrappedMintKey(
|
||||||
tokenBridgeProgramId: PublicKeyInitData,
|
tokenBridgeProgramId: PublicKeyInitData,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
import { createReadOnlyTokenBridgeProgramInterface } from "../program";
|
import { createReadOnlyTokenBridgeProgramInterface } from "../program";
|
||||||
import { getPostMessageAccounts } from "../../wormhole";
|
import { getPostMessageAccounts } from "../../wormhole";
|
||||||
import {
|
import {
|
||||||
deriveSplTokenMetadataKey,
|
deriveTokenMetadataKey,
|
||||||
deriveTokenBridgeConfigKey,
|
deriveTokenBridgeConfigKey,
|
||||||
deriveWrappedMetaKey,
|
deriveWrappedMetaKey,
|
||||||
} from "../accounts";
|
} from "../accounts";
|
||||||
|
@ -83,7 +83,7 @@ export function getAttestTokenAccounts(
|
||||||
config: deriveTokenBridgeConfigKey(tokenBridgeProgramId),
|
config: deriveTokenBridgeConfigKey(tokenBridgeProgramId),
|
||||||
mint: new PublicKey(mint),
|
mint: new PublicKey(mint),
|
||||||
wrappedMeta: deriveWrappedMetaKey(tokenBridgeProgramId, mint),
|
wrappedMeta: deriveWrappedMetaKey(tokenBridgeProgramId, mint),
|
||||||
splMetadata: deriveSplTokenMetadataKey(mint),
|
splMetadata: deriveTokenMetadataKey(mint),
|
||||||
wormholeBridge,
|
wormholeBridge,
|
||||||
wormholeMessage: new PublicKey(message),
|
wormholeMessage: new PublicKey(message),
|
||||||
wormholeEmitter,
|
wormholeEmitter,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
import {
|
import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
PublicKeyInitData,
|
PublicKeyInitData,
|
||||||
|
@ -5,24 +6,23 @@ import {
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
||||||
import { createReadOnlyTokenBridgeProgramInterface } from "../program";
|
|
||||||
import { deriveClaimKey, derivePostedVaaKey } from "../../wormhole";
|
|
||||||
import {
|
|
||||||
deriveEndpointKey,
|
|
||||||
deriveMintAuthorityKey,
|
|
||||||
deriveSplTokenMetadataKey,
|
|
||||||
deriveWrappedMetaKey,
|
|
||||||
deriveTokenBridgeConfigKey,
|
|
||||||
deriveWrappedMintKey,
|
|
||||||
} from "../accounts";
|
|
||||||
import {
|
import {
|
||||||
isBytes,
|
isBytes,
|
||||||
parseAttestMetaVaa,
|
parseAttestMetaVaa,
|
||||||
ParsedAttestMetaVaa,
|
ParsedAttestMetaVaa,
|
||||||
SignedVaa,
|
SignedVaa,
|
||||||
} from "../../../vaa";
|
} from "../../../vaa";
|
||||||
import { SplTokenMetadataProgram } from "../../utils";
|
import { TOKEN_METADATA_PROGRAM_ID } from "../../utils";
|
||||||
|
import { deriveClaimKey, derivePostedVaaKey } from "../../wormhole";
|
||||||
|
import {
|
||||||
|
deriveEndpointKey,
|
||||||
|
deriveMintAuthorityKey,
|
||||||
|
deriveTokenBridgeConfigKey,
|
||||||
|
deriveTokenMetadataKey,
|
||||||
|
deriveWrappedMetaKey,
|
||||||
|
deriveWrappedMintKey,
|
||||||
|
} from "../accounts";
|
||||||
|
import { createReadOnlyTokenBridgeProgramInterface } from "../program";
|
||||||
|
|
||||||
export function createCreateWrappedInstruction(
|
export function createCreateWrappedInstruction(
|
||||||
tokenBridgeProgramId: PublicKeyInitData,
|
tokenBridgeProgramId: PublicKeyInitData,
|
||||||
|
@ -96,12 +96,12 @@ export function getCreateWrappedAccounts(
|
||||||
),
|
),
|
||||||
mint,
|
mint,
|
||||||
wrappedMeta: deriveWrappedMetaKey(tokenBridgeProgramId, mint),
|
wrappedMeta: deriveWrappedMetaKey(tokenBridgeProgramId, mint),
|
||||||
splMetadata: deriveSplTokenMetadataKey(mint),
|
splMetadata: deriveTokenMetadataKey(mint),
|
||||||
mintAuthority: deriveMintAuthorityKey(tokenBridgeProgramId),
|
mintAuthority: deriveMintAuthorityKey(tokenBridgeProgramId),
|
||||||
rent: SYSVAR_RENT_PUBKEY,
|
rent: SYSVAR_RENT_PUBKEY,
|
||||||
systemProgram: SystemProgram.programId,
|
systemProgram: SystemProgram.programId,
|
||||||
tokenProgram: TOKEN_PROGRAM_ID,
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
splMetadataProgram: SplTokenMetadataProgram.programId,
|
splMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
|
||||||
wormholeProgram: new PublicKey(wormholeProgramId),
|
wormholeProgram: new PublicKey(wormholeProgramId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,5 @@ export * from "./account";
|
||||||
export * from "./bpfLoaderUpgradeable";
|
export * from "./bpfLoaderUpgradeable";
|
||||||
export * from "./connection";
|
export * from "./connection";
|
||||||
export * from "./secp256k1";
|
export * from "./secp256k1";
|
||||||
export * from "./splMetadata";
|
export * from "./tokenMetadata";
|
||||||
export * from "./transaction";
|
export * from "./transaction";
|
||||||
|
|
|
@ -1,331 +0,0 @@
|
||||||
import {
|
|
||||||
AccountMeta,
|
|
||||||
Commitment,
|
|
||||||
Connection,
|
|
||||||
PublicKey,
|
|
||||||
PublicKeyInitData,
|
|
||||||
SystemProgram,
|
|
||||||
SYSVAR_RENT_PUBKEY,
|
|
||||||
TransactionInstruction,
|
|
||||||
} from "@solana/web3.js";
|
|
||||||
import {
|
|
||||||
deriveAddress,
|
|
||||||
getAccountData,
|
|
||||||
newAccountMeta,
|
|
||||||
newReadOnlyAccountMeta,
|
|
||||||
} from "./account";
|
|
||||||
|
|
||||||
export class Creator {
|
|
||||||
address: PublicKey;
|
|
||||||
verified: boolean;
|
|
||||||
share: number;
|
|
||||||
|
|
||||||
constructor(address: PublicKeyInitData, verified: boolean, share: number) {
|
|
||||||
this.address = new PublicKey(address);
|
|
||||||
this.verified = verified;
|
|
||||||
this.share = share;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size: number = 34;
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
const serialized = Buffer.alloc(Creator.size);
|
|
||||||
serialized.write(this.address.toBuffer().toString("hex"), 0, "hex");
|
|
||||||
if (this.verified) {
|
|
||||||
serialized.writeUInt8(1, 32);
|
|
||||||
}
|
|
||||||
serialized.writeUInt8(this.share, 33);
|
|
||||||
return serialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(data: Buffer): Creator {
|
|
||||||
const address = data.subarray(0, 32);
|
|
||||||
const verified = data.readUInt8(32) > 0;
|
|
||||||
const share = data.readUInt8(33);
|
|
||||||
return new Creator(address, verified, share);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Data {
|
|
||||||
name: string;
|
|
||||||
symbol: string;
|
|
||||||
uri: string;
|
|
||||||
sellerFeeBasisPoints: number;
|
|
||||||
creators: Creator[] | null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
uri: string,
|
|
||||||
sellerFeeBasisPoints: number,
|
|
||||||
creators: Creator[] | null
|
|
||||||
) {
|
|
||||||
this.name = name;
|
|
||||||
this.symbol = symbol;
|
|
||||||
this.uri = uri;
|
|
||||||
this.sellerFeeBasisPoints = sellerFeeBasisPoints;
|
|
||||||
this.creators = creators;
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
const nameLen = this.name.length;
|
|
||||||
const symbolLen = this.symbol.length;
|
|
||||||
const uriLen = this.uri.length;
|
|
||||||
const creators = this.creators;
|
|
||||||
const [creatorsLen, creatorsSize] = (() => {
|
|
||||||
if (creators === null) {
|
|
||||||
return [0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const creatorsLen = creators.length;
|
|
||||||
return [creatorsLen, 4 + creatorsLen * Creator.size];
|
|
||||||
})();
|
|
||||||
const serialized = Buffer.alloc(
|
|
||||||
15 + nameLen + symbolLen + uriLen + creatorsSize
|
|
||||||
);
|
|
||||||
serialized.writeUInt32LE(nameLen, 0);
|
|
||||||
serialized.write(this.name, 4);
|
|
||||||
serialized.writeUInt32LE(symbolLen, 4 + nameLen);
|
|
||||||
serialized.write(this.symbol, 8 + nameLen);
|
|
||||||
serialized.writeUInt32LE(uriLen, 8 + nameLen + symbolLen);
|
|
||||||
serialized.write(this.uri, 12 + nameLen + symbolLen);
|
|
||||||
serialized.writeUInt16LE(
|
|
||||||
this.sellerFeeBasisPoints,
|
|
||||||
12 + nameLen + symbolLen + uriLen
|
|
||||||
);
|
|
||||||
if (creators === null) {
|
|
||||||
serialized.writeUInt8(0, 14 + nameLen + symbolLen + uriLen);
|
|
||||||
} else {
|
|
||||||
serialized.writeUInt8(1, 14 + nameLen + symbolLen + uriLen);
|
|
||||||
serialized.writeUInt32LE(creatorsLen, 15 + nameLen + symbolLen + uriLen);
|
|
||||||
for (let i = 0; i < creatorsLen; ++i) {
|
|
||||||
const creator = creators.at(i)!;
|
|
||||||
const idx = 19 + nameLen + symbolLen + uriLen + i * Creator.size;
|
|
||||||
serialized.write(creator.serialize().toString("hex"), idx, "hex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(data: Buffer): Data {
|
|
||||||
const nameLen = data.readUInt32LE(0);
|
|
||||||
const name = data.subarray(4, 4 + nameLen).toString();
|
|
||||||
const symbolLen = data.readUInt32LE(4 + nameLen);
|
|
||||||
const symbol = data
|
|
||||||
.subarray(8 + nameLen, 8 + nameLen + symbolLen)
|
|
||||||
.toString();
|
|
||||||
const uriLen = data.readUInt32LE(8 + nameLen + symbolLen);
|
|
||||||
const uri = data
|
|
||||||
.subarray(12 + nameLen + symbolLen, 12 + nameLen + symbolLen + uriLen)
|
|
||||||
.toString();
|
|
||||||
const sellerFeeBasisPoints = data.readUInt16LE(
|
|
||||||
12 + nameLen + symbolLen + uriLen
|
|
||||||
);
|
|
||||||
const optionCreators = data.readUInt8(14 + nameLen + symbolLen + uriLen);
|
|
||||||
const creators = (() => {
|
|
||||||
if (optionCreators == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const creators: Creator[] = [];
|
|
||||||
const creatorsLen = data.readUInt32LE(15 + nameLen + symbolLen + uriLen);
|
|
||||||
for (let i = 0; i < creatorsLen; ++i) {
|
|
||||||
const idx = 19 + nameLen + symbolLen + uriLen + i * Creator.size;
|
|
||||||
creators.push(
|
|
||||||
Creator.deserialize(data.subarray(idx, idx + Creator.size))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return creators;
|
|
||||||
})();
|
|
||||||
return new Data(name, symbol, uri, sellerFeeBasisPoints, creators);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CreateMetadataAccountArgs extends Data {
|
|
||||||
isMutable: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
uri: string,
|
|
||||||
sellerFeeBasisPoints: number,
|
|
||||||
creators: Creator[] | null,
|
|
||||||
isMutable: boolean
|
|
||||||
) {
|
|
||||||
super(name, symbol, uri, sellerFeeBasisPoints, creators);
|
|
||||||
this.isMutable = isMutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static serialize(
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
uri: string,
|
|
||||||
sellerFeeBasisPoints: number,
|
|
||||||
creators: Creator[] | null,
|
|
||||||
isMutable: boolean
|
|
||||||
) {
|
|
||||||
return new CreateMetadataAccountArgs(
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
uri,
|
|
||||||
sellerFeeBasisPoints,
|
|
||||||
creators,
|
|
||||||
isMutable
|
|
||||||
).serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
static serializeInstructionData(
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
uri: string,
|
|
||||||
sellerFeeBasisPoints: number,
|
|
||||||
creators: Creator[] | null,
|
|
||||||
isMutable: boolean
|
|
||||||
) {
|
|
||||||
return Buffer.concat([
|
|
||||||
Buffer.alloc(1, 0),
|
|
||||||
CreateMetadataAccountArgs.serialize(
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
uri,
|
|
||||||
sellerFeeBasisPoints,
|
|
||||||
creators,
|
|
||||||
isMutable
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
return Buffer.concat([
|
|
||||||
super.serialize(),
|
|
||||||
Buffer.alloc(1, this.isMutable ? 1 : 0),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SplTokenMetadataProgram {
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public key that identifies the SPL Token Metadata program
|
|
||||||
*/
|
|
||||||
static programId: PublicKey = new PublicKey(
|
|
||||||
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
|
|
||||||
);
|
|
||||||
|
|
||||||
static createMetadataAccounts(
|
|
||||||
payer: PublicKey,
|
|
||||||
mint: PublicKey,
|
|
||||||
mintAuthority: PublicKey,
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
updateAuthority: PublicKey,
|
|
||||||
updateAuthorityIsSigner: boolean = false,
|
|
||||||
uri?: string,
|
|
||||||
creators?: Creator[] | null,
|
|
||||||
sellerFeeBasisPoints?: number,
|
|
||||||
isMutable: boolean = false,
|
|
||||||
metadataAccount: PublicKey = deriveSplTokenMetadataKey(mint)
|
|
||||||
): TransactionInstruction {
|
|
||||||
const keys: AccountMeta[] = [
|
|
||||||
newAccountMeta(metadataAccount, false),
|
|
||||||
newReadOnlyAccountMeta(mint, false),
|
|
||||||
newReadOnlyAccountMeta(mintAuthority, true),
|
|
||||||
newReadOnlyAccountMeta(payer, true),
|
|
||||||
newReadOnlyAccountMeta(updateAuthority, updateAuthorityIsSigner),
|
|
||||||
newReadOnlyAccountMeta(SystemProgram.programId, false),
|
|
||||||
newReadOnlyAccountMeta(SYSVAR_RENT_PUBKEY, false),
|
|
||||||
];
|
|
||||||
const data = CreateMetadataAccountArgs.serializeInstructionData(
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
uri === undefined ? "" : uri,
|
|
||||||
sellerFeeBasisPoints === undefined ? 0 : sellerFeeBasisPoints,
|
|
||||||
creators === undefined ? null : creators,
|
|
||||||
isMutable
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
programId: SplTokenMetadataProgram.programId,
|
|
||||||
keys,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deriveSplTokenMetadataKey(mint: PublicKeyInitData): PublicKey {
|
|
||||||
return deriveAddress(
|
|
||||||
[
|
|
||||||
Buffer.from("metadata"),
|
|
||||||
SplTokenMetadataProgram.programId.toBuffer(),
|
|
||||||
new PublicKey(mint).toBuffer(),
|
|
||||||
],
|
|
||||||
SplTokenMetadataProgram.programId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Key {
|
|
||||||
Uninitialized,
|
|
||||||
EditionV1,
|
|
||||||
MasterEditionV1,
|
|
||||||
ReservationListV1,
|
|
||||||
MetadataV1,
|
|
||||||
ReservationListV2,
|
|
||||||
MasterEditionV2,
|
|
||||||
EditionMarker,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Metadata {
|
|
||||||
key: Key;
|
|
||||||
updateAuthority: PublicKey;
|
|
||||||
mint: PublicKey;
|
|
||||||
data: Data;
|
|
||||||
primarySaleHappened: boolean;
|
|
||||||
isMutable: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
key: number,
|
|
||||||
updateAuthority: PublicKeyInitData,
|
|
||||||
mint: PublicKeyInitData,
|
|
||||||
data: Data,
|
|
||||||
primarySaleHappened: boolean,
|
|
||||||
isMutable: boolean
|
|
||||||
) {
|
|
||||||
this.key = key as Key;
|
|
||||||
this.updateAuthority = new PublicKey(updateAuthority);
|
|
||||||
this.mint = new PublicKey(mint);
|
|
||||||
this.data = data;
|
|
||||||
this.primarySaleHappened = primarySaleHappened;
|
|
||||||
this.isMutable = isMutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(data: Buffer): Metadata {
|
|
||||||
const key = data.readUInt8(0);
|
|
||||||
const updateAuthority = data.subarray(1, 33);
|
|
||||||
const mint = data.subarray(33, 65);
|
|
||||||
const meta = Data.deserialize(data.subarray(65));
|
|
||||||
const metaLen = meta.serialize().length;
|
|
||||||
const primarySaleHappened = data.readUInt8(65 + metaLen) > 0;
|
|
||||||
const isMutable = data.readUInt8(66 + metaLen) > 0;
|
|
||||||
return new Metadata(
|
|
||||||
key,
|
|
||||||
updateAuthority,
|
|
||||||
mint,
|
|
||||||
meta,
|
|
||||||
primarySaleHappened,
|
|
||||||
isMutable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMetadata(
|
|
||||||
connection: Connection,
|
|
||||||
mint: PublicKeyInitData,
|
|
||||||
commitment?: Commitment
|
|
||||||
): Promise<Metadata> {
|
|
||||||
return connection
|
|
||||||
.getAccountInfo(deriveSplTokenMetadataKey(mint), commitment)
|
|
||||||
.then((info) => Metadata.deserialize(getAccountData(info)));
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { PublicKey, PublicKeyInitData } from "@solana/web3.js";
|
||||||
|
import { deriveAddress } from "./account";
|
||||||
|
|
||||||
|
export const TOKEN_METADATA_PROGRAM_ID = new PublicKey(
|
||||||
|
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
|
||||||
|
);
|
||||||
|
|
||||||
|
export function deriveTokenMetadataKey(mint: PublicKeyInitData): PublicKey {
|
||||||
|
return deriveAddress(
|
||||||
|
[
|
||||||
|
Buffer.from("metadata"),
|
||||||
|
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
|
||||||
|
new PublicKey(mint).toBuffer(),
|
||||||
|
],
|
||||||
|
TOKEN_METADATA_PROGRAM_ID
|
||||||
|
);
|
||||||
|
}
|
|
@ -1847,6 +1847,19 @@ dependencies = [
|
||||||
"syn 1.0.91",
|
"syn 1.0.91",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mpl-token-metadata"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "git+https://github.com/wormhole-foundation/metaplex-program-library?rev=a7ab32ab0defd89c98f205c80ebdaf77ed60152d#a7ab32ab0defd89c98f205c80ebdaf77ed60152d"
|
||||||
|
dependencies = [
|
||||||
|
"borsh",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
"solana-program",
|
||||||
|
"spl-token",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nft-bridge"
|
name = "nft-bridge"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1857,6 +1870,7 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
|
"mpl-token-metadata",
|
||||||
"primitive-types",
|
"primitive-types",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"rocksalt",
|
"rocksalt",
|
||||||
|
@ -1868,7 +1882,6 @@ dependencies = [
|
||||||
"solitaire",
|
"solitaire",
|
||||||
"spl-associated-token-account",
|
"spl-associated-token-account",
|
||||||
"spl-token",
|
"spl-token",
|
||||||
"spl-token-metadata",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wormhole-bridge-solana",
|
"wormhole-bridge-solana",
|
||||||
]
|
]
|
||||||
|
@ -3985,15 +3998,6 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spl-token-metadata"
|
|
||||||
version = "0.0.1"
|
|
||||||
dependencies = [
|
|
||||||
"borsh",
|
|
||||||
"solana-program",
|
|
||||||
"spl-token",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -4269,6 +4273,7 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
|
"mpl-token-metadata",
|
||||||
"primitive-types",
|
"primitive-types",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"rocksalt",
|
"rocksalt",
|
||||||
|
@ -4279,7 +4284,6 @@ dependencies = [
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
"solitaire",
|
"solitaire",
|
||||||
"spl-token",
|
"spl-token",
|
||||||
"spl-token-metadata",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wormhole-bridge-solana",
|
"wormhole-bridge-solana",
|
||||||
]
|
]
|
||||||
|
@ -4292,6 +4296,7 @@ dependencies = [
|
||||||
"borsh",
|
"borsh",
|
||||||
"clap",
|
"clap",
|
||||||
"hex",
|
"hex",
|
||||||
|
"mpl-token-metadata",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"solana-clap-utils",
|
"solana-clap-utils",
|
||||||
|
@ -4300,7 +4305,6 @@ dependencies = [
|
||||||
"solana-program",
|
"solana-program",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
"solitaire",
|
"solitaire",
|
||||||
"spl-token-metadata",
|
|
||||||
"token-bridge",
|
"token-bridge",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ COPY migration migration
|
||||||
COPY Cargo.toml Cargo.toml
|
COPY Cargo.toml Cargo.toml
|
||||||
COPY Cargo.lock Cargo.lock
|
COPY Cargo.lock Cargo.lock
|
||||||
COPY solitaire solitaire
|
COPY solitaire solitaire
|
||||||
|
COPY external external
|
||||||
|
|
||||||
ENV RUST_LOG="solana_runtime::system_instruction_processor=trace,solana_runtime::message_processor=trace,solana_bpf_loader=debug,solana_rbpf=debug"
|
ENV RUST_LOG="solana_runtime::system_instruction_processor=trace,solana_runtime::message_processor=trace,solana_bpf_loader=debug,solana_rbpf=debug"
|
||||||
ENV RUST_BACKTRACE=1
|
ENV RUST_BACKTRACE=1
|
||||||
|
@ -40,7 +41,7 @@ RUN --mount=type=cache,target=target,id=build \
|
||||||
cp target/deploy/wormhole_migration.so /opt/solana/deps/wormhole_migration.so && \
|
cp target/deploy/wormhole_migration.so /opt/solana/deps/wormhole_migration.so && \
|
||||||
cp target/deploy/token_bridge.so /opt/solana/deps/token_bridge.so && \
|
cp target/deploy/token_bridge.so /opt/solana/deps/token_bridge.so && \
|
||||||
cp target/deploy/nft_bridge.so /opt/solana/deps/nft_bridge.so && \
|
cp target/deploy/nft_bridge.so /opt/solana/deps/nft_bridge.so && \
|
||||||
cp modules/token_bridge/token-metadata/spl_token_metadata.so /opt/solana/deps/spl_token_metadata.so
|
cp external/mpl_token_metadata.so /opt/solana/deps/mpl_token_metadata.so
|
||||||
|
|
||||||
FROM scratch AS export-stage
|
FROM scratch AS export-stage
|
||||||
COPY --from=builder /opt/solana/deps /
|
COPY --from=builder /opt/solana/deps /
|
||||||
|
|
Binary file not shown.
|
@ -29,7 +29,7 @@ solana-program = "*"
|
||||||
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
||||||
spl-associated-token-account = { version = "1.0.2", features = ["no-entrypoint"] }
|
spl-associated-token-account = { version = "1.0.2", features = ["no-entrypoint"] }
|
||||||
primitive-types = { version = "0.9.0", default-features = false }
|
primitive-types = { version = "0.9.0", default-features = false }
|
||||||
spl-token-metadata = { path = "../../token_bridge/token-metadata" }
|
spl-token-metadata = { git = "https://github.com/wormhole-foundation/metaplex-program-library", rev = "a7ab32ab0defd89c98f205c80ebdaf77ed60152d", package = "mpl-token-metadata" }
|
||||||
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true }
|
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
@ -40,5 +40,3 @@ libsecp256k1 = { version = "0.6.0", features = [] }
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
solana-program-test = "=1.10.31"
|
solana-program-test = "=1.10.31"
|
||||||
solana-sdk = "=1.10.31"
|
solana-sdk = "=1.10.31"
|
||||||
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
|
||||||
spl-token-metadata = { path = "../../token_bridge/token-metadata" }
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::types::*;
|
use crate::{
|
||||||
|
types::*,
|
||||||
|
TokenBridgeError,
|
||||||
|
};
|
||||||
use bridge::{
|
use bridge::{
|
||||||
accounts::BridgeData,
|
accounts::BridgeData,
|
||||||
api::ForeignAddress,
|
api::ForeignAddress,
|
||||||
|
@ -9,6 +12,7 @@ use solitaire::{
|
||||||
processors::seeded::Seeded,
|
processors::seeded::Seeded,
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
use spl_token_metadata::state::Key::MetadataV1;
|
||||||
|
|
||||||
pub type AuthoritySigner<'b> = Derive<Info<'b>, "authority_signer">;
|
pub type AuthoritySigner<'b> = Derive<Info<'b>, "authority_signer">;
|
||||||
pub type CustodySigner<'b> = Derive<Info<'b>, "custody_signer">;
|
pub type CustodySigner<'b> = Derive<Info<'b>, "custody_signer">;
|
||||||
|
@ -106,3 +110,44 @@ impl<'b> Seeded<&SplTokenMetaDerivationData> for SplTokenMeta<'b> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method removes code duplication when checking token metadata. When metadata is read for
|
||||||
|
/// attestation and transfers, Token Bridge does not invoke Metaplex's Token Metadata program, so
|
||||||
|
/// it must validate the account the same way Token Metadata program does to ensure the correct
|
||||||
|
/// account is passed into Token Bridge's instruction context.
|
||||||
|
pub fn deserialize_and_verify_metadata(
|
||||||
|
info: &Info,
|
||||||
|
derivation_data: SplTokenMetaDerivationData,
|
||||||
|
) -> Result<spl_token_metadata::state::Metadata> {
|
||||||
|
// Verify pda.
|
||||||
|
info.verify_derivation(&spl_token_metadata::id(), &derivation_data)?;
|
||||||
|
|
||||||
|
// There must be account data for token's metadata.
|
||||||
|
if info.data_is_empty() {
|
||||||
|
return Err(TokenBridgeError::NonexistentTokenMetadataAccount.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account must belong to Metaplex Token Metadata program.
|
||||||
|
if *info.owner != spl_token_metadata::id() {
|
||||||
|
return Err(TokenBridgeError::WrongAccountOwner.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account must be the expected Metadata length.
|
||||||
|
if info.data_len() != spl_token_metadata::state::MAX_METADATA_LEN {
|
||||||
|
return Err(TokenBridgeError::InvalidMetadata.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data: &[u8] = &info.data.borrow_mut();
|
||||||
|
|
||||||
|
// Unfortunately we cannot use `map_err` easily, so we will match certain deserialization conditions.
|
||||||
|
match spl_token_metadata::utils::meta_deser_unchecked(&mut data) {
|
||||||
|
Ok(deserialized) => {
|
||||||
|
if deserialized.key == MetadataV1 {
|
||||||
|
Ok(deserialized)
|
||||||
|
} else {
|
||||||
|
Err(TokenBridgeError::NotMetadataV1Account.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(TokenBridgeError::InvalidMetadata.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -382,7 +382,7 @@ pub fn complete_wrapped_meta(
|
||||||
symbol.retain(|&c| c != '\u{FFFD}');
|
symbol.retain(|&c| c != '\u{FFFD}');
|
||||||
let symbol: String = symbol.iter().collect();
|
let symbol: String = symbol.iter().collect();
|
||||||
|
|
||||||
let spl_token_metadata_ix = spl_token_metadata::instruction::create_metadata_accounts(
|
let spl_token_metadata_ix = spl_token_metadata::instruction::create_metadata_accounts_v3(
|
||||||
spl_token_metadata::id(),
|
spl_token_metadata::id(),
|
||||||
*accs.spl_metadata.key,
|
*accs.spl_metadata.key,
|
||||||
*accs.mint.info().key,
|
*accs.mint.info().key,
|
||||||
|
@ -396,6 +396,9 @@ pub fn complete_wrapped_meta(
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
invoke_seeded(&spl_token_metadata_ix, ctx, &accs.mint_authority, None)?;
|
invoke_seeded(&spl_token_metadata_ix, ctx, &accs.mint_authority, None)?;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{
|
accounts::{
|
||||||
|
deserialize_and_verify_metadata,
|
||||||
AuthoritySigner,
|
AuthoritySigner,
|
||||||
ConfigAccount,
|
ConfigAccount,
|
||||||
CoreBridge,
|
CoreBridge,
|
||||||
|
@ -17,11 +18,7 @@ use crate::{
|
||||||
messages::PayloadTransfer,
|
messages::PayloadTransfer,
|
||||||
types::*,
|
types::*,
|
||||||
TokenBridgeError,
|
TokenBridgeError,
|
||||||
TokenBridgeError::{
|
TokenBridgeError::WrongAccountOwner,
|
||||||
InvalidMetadata,
|
|
||||||
TokenNotNFT,
|
|
||||||
WrongAccountOwner,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use bridge::{
|
use bridge::{
|
||||||
api::PostMessageData,
|
api::PostMessageData,
|
||||||
|
@ -50,7 +47,6 @@ use solitaire::{
|
||||||
CreationLamports::Exempt,
|
CreationLamports::Exempt,
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use spl_token_metadata::state::Metadata;
|
|
||||||
|
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct TransferNative<'b> {
|
pub struct TransferNative<'b> {
|
||||||
|
@ -123,24 +119,11 @@ pub fn transfer_native(
|
||||||
accs.custody
|
accs.custody
|
||||||
.verify_derivation(ctx.program_id, &derivation_data)?;
|
.verify_derivation(ctx.program_id, &derivation_data)?;
|
||||||
|
|
||||||
let derivation_data: SplTokenMetaDerivationData = (&*accs).into();
|
|
||||||
accs.spl_metadata
|
|
||||||
.verify_derivation(&spl_token_metadata::id(), &derivation_data)?;
|
|
||||||
|
|
||||||
// Verify mints
|
// Verify mints
|
||||||
if accs.from.mint != *accs.mint.info().key {
|
if accs.from.mint != *accs.mint.info().key {
|
||||||
return Err(TokenBridgeError::InvalidMint.into());
|
return Err(TokenBridgeError::InvalidMint.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token must have metadata
|
|
||||||
if accs.spl_metadata.data_is_empty() {
|
|
||||||
return Err(TokenNotNFT.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if *accs.spl_metadata.owner != spl_token_metadata::id() {
|
|
||||||
return Err(WrongAccountOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the token is not a wrapped token
|
// Verify that the token is not a wrapped token
|
||||||
if let COption::Some(mint_authority) = accs.mint.mint_authority {
|
if let COption::Some(mint_authority) = accs.mint.mint_authority {
|
||||||
if mint_authority == MintSigner::key(None, ctx.program_id) {
|
if mint_authority == MintSigner::key(None, ctx.program_id) {
|
||||||
|
@ -180,8 +163,7 @@ pub fn transfer_native(
|
||||||
);
|
);
|
||||||
invoke(&transfer_ix, ctx.accounts)?;
|
invoke(&transfer_ix, ctx.accounts)?;
|
||||||
|
|
||||||
let metadata: Metadata =
|
let metadata = deserialize_and_verify_metadata(&accs.spl_metadata, (&*accs).into())?;
|
||||||
Metadata::from_account_info(accs.spl_metadata.info()).ok_or(InvalidMetadata)?;
|
|
||||||
|
|
||||||
// Post message
|
// Post message
|
||||||
// Given there is no tokenID equivalent on Solana and each distinct token address is translated
|
// Given there is no tokenID equivalent on Solana and each distinct token address is translated
|
||||||
|
@ -326,21 +308,7 @@ pub fn transfer_wrapped(
|
||||||
accs.wrapped_meta
|
accs.wrapped_meta
|
||||||
.verify_derivation(ctx.program_id, &derivation_data)?;
|
.verify_derivation(ctx.program_id, &derivation_data)?;
|
||||||
|
|
||||||
// Token must have metadata
|
let metadata = deserialize_and_verify_metadata(&accs.spl_metadata, (&*accs).into())?;
|
||||||
if accs.spl_metadata.data_is_empty() {
|
|
||||||
return Err(TokenNotNFT.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let derivation_data: SplTokenMetaDerivationData = (&*accs).into();
|
|
||||||
accs.spl_metadata
|
|
||||||
.verify_derivation(&spl_token_metadata::id(), &derivation_data)?;
|
|
||||||
|
|
||||||
if *accs.spl_metadata.owner != spl_token_metadata::id() {
|
|
||||||
return Err(WrongAccountOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let metadata: Metadata =
|
|
||||||
Metadata::from_account_info(accs.spl_metadata.info()).ok_or(InvalidMetadata)?;
|
|
||||||
|
|
||||||
// Post message
|
// Post message
|
||||||
let payload = PayloadTransfer {
|
let payload = PayloadTransfer {
|
||||||
|
|
|
@ -59,9 +59,10 @@ pub enum TokenBridgeError {
|
||||||
TokenNotNative,
|
TokenNotNative,
|
||||||
UninitializedMint,
|
UninitializedMint,
|
||||||
WrongAccountOwner,
|
WrongAccountOwner,
|
||||||
TokenNotNFT,
|
NonexistentTokenMetadataAccount,
|
||||||
InvalidAssociatedAccount,
|
InvalidAssociatedAccount,
|
||||||
InvalidRecipient,
|
InvalidRecipient,
|
||||||
|
NotMetadataV1Account,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TokenBridgeError> for SolitaireError {
|
impl From<TokenBridgeError> for SolitaireError {
|
||||||
|
|
|
@ -128,7 +128,7 @@ mod helpers {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut builder = ProgramTest::new("bridge", program, processor!(bridge::solitaire));
|
let mut builder = ProgramTest::new("bridge", program, processor!(bridge::solitaire));
|
||||||
builder.add_program("spl_token_metadata", spl_token_metadata::id(), None);
|
builder.add_program("mpl_token_metadata", spl_token_metadata::id(), None);
|
||||||
builder.add_program(
|
builder.add_program(
|
||||||
"nft_bridge",
|
"nft_bridge",
|
||||||
token_program,
|
token_program,
|
||||||
|
@ -506,21 +506,26 @@ mod helpers {
|
||||||
client,
|
client,
|
||||||
payer,
|
payer,
|
||||||
&[payer, mint_authority],
|
&[payer, mint_authority],
|
||||||
&[spl_token_metadata::instruction::create_metadata_accounts(
|
&[
|
||||||
spl_token_metadata::id(),
|
spl_token_metadata::instruction::create_metadata_accounts_v3(
|
||||||
metadata_account,
|
spl_token_metadata::id(),
|
||||||
mint,
|
metadata_account,
|
||||||
mint_authority.pubkey(),
|
mint,
|
||||||
payer.pubkey(),
|
mint_authority.pubkey(),
|
||||||
update_authority,
|
payer.pubkey(),
|
||||||
name,
|
update_authority,
|
||||||
symbol,
|
name,
|
||||||
uri,
|
symbol,
|
||||||
None,
|
uri,
|
||||||
0,
|
None,
|
||||||
false,
|
0,
|
||||||
false,
|
false,
|
||||||
)],
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
],
|
||||||
CommitmentLevel::Processed,
|
CommitmentLevel::Processed,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -17,4 +17,4 @@ solana-cli-config = "=1.10.31"
|
||||||
solitaire = { path = "../../../solitaire/program" }
|
solitaire = { path = "../../../solitaire/program" }
|
||||||
solana-clap-utils = "=1.10.31"
|
solana-clap-utils = "=1.10.31"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
spl-token-metadata = { path = "../token-metadata" }
|
spl-token-metadata = { git = "https://github.com/wormhole-foundation/metaplex-program-library", rev = "a7ab32ab0defd89c98f205c80ebdaf77ed60152d", package = "mpl-token-metadata" }
|
||||||
|
|
|
@ -107,7 +107,7 @@ fn command_create_meta(
|
||||||
)
|
)
|
||||||
.0;
|
.0;
|
||||||
println!("Meta account: {}", meta_acc);
|
println!("Meta account: {}", meta_acc);
|
||||||
let ix = spl_token_metadata::instruction::create_metadata_accounts(
|
let ix = spl_token_metadata::instruction::create_metadata_accounts_v3(
|
||||||
spl_token_metadata::id(),
|
spl_token_metadata::id(),
|
||||||
meta_acc,
|
meta_acc,
|
||||||
*mint,
|
*mint,
|
||||||
|
@ -121,6 +121,9 @@ fn command_create_meta(
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
|
@ -334,9 +337,15 @@ fn main() {
|
||||||
&spl_token_metadata::id(),
|
&spl_token_metadata::id(),
|
||||||
)
|
)
|
||||||
.0;
|
.0;
|
||||||
let meta_info = config.rpc_client.get_account(&meta_acc).unwrap();
|
let meta_info = spl_token_metadata::utils::meta_deser_unchecked(
|
||||||
let meta_info =
|
&mut config
|
||||||
spl_token_metadata::state::Metadata::from_bytes(&meta_info.data).unwrap();
|
.rpc_client
|
||||||
|
.get_account(&meta_acc)
|
||||||
|
.unwrap()
|
||||||
|
.data
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
println!("Key: {:?}", meta_info.key);
|
println!("Key: {:?}", meta_info.key);
|
||||||
println!("Mint: {}", meta_info.mint);
|
println!("Mint: {}", meta_info.mint);
|
||||||
println!("Metadata Key: {}", meta_acc);
|
println!("Metadata Key: {}", meta_acc);
|
||||||
|
|
|
@ -28,7 +28,7 @@ sha3 = "0.9.1"
|
||||||
solana-program = "*"
|
solana-program = "*"
|
||||||
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
||||||
primitive-types = { version = "0.9.0", default-features = false }
|
primitive-types = { version = "0.9.0", default-features = false }
|
||||||
spl-token-metadata = { path = "../token-metadata" }
|
spl-token-metadata = { git = "https://github.com/wormhole-foundation/metaplex-program-library", rev = "a7ab32ab0defd89c98f205c80ebdaf77ed60152d", package = "mpl-token-metadata" }
|
||||||
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true }
|
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
@ -39,5 +39,3 @@ libsecp256k1 = { version = "0.6.0", features = [] }
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
solana-program-test = "=1.10.31"
|
solana-program-test = "=1.10.31"
|
||||||
solana-sdk = "=1.10.31"
|
solana-sdk = "=1.10.31"
|
||||||
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
|
||||||
spl-token-metadata = { path = "../token-metadata" }
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::types::*;
|
use crate::{
|
||||||
|
types::*,
|
||||||
|
TokenBridgeError,
|
||||||
|
};
|
||||||
use bridge::{
|
use bridge::{
|
||||||
accounts::BridgeData,
|
accounts::BridgeData,
|
||||||
api::ForeignAddress,
|
api::ForeignAddress,
|
||||||
|
@ -8,6 +11,7 @@ use solitaire::{
|
||||||
processors::seeded::Seeded,
|
processors::seeded::Seeded,
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
use spl_token_metadata::state::Key::MetadataV1;
|
||||||
|
|
||||||
pub type AuthoritySigner<'b> = Derive<Info<'b>, "authority_signer">;
|
pub type AuthoritySigner<'b> = Derive<Info<'b>, "authority_signer">;
|
||||||
pub type CustodySigner<'b> = Derive<Info<'b>, "custody_signer">;
|
pub type CustodySigner<'b> = Derive<Info<'b>, "custody_signer">;
|
||||||
|
@ -101,3 +105,44 @@ impl<'b> Seeded<&SplTokenMetaDerivationData> for SplTokenMeta<'b> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method removes code duplication when checking token metadata. When metadata is read for
|
||||||
|
/// attestation and transfers, Token Bridge does not invoke Metaplex's Token Metadata program, so
|
||||||
|
/// it must validate the account the same way Token Metadata program does to ensure the correct
|
||||||
|
/// account is passed into Token Bridge's instruction context.
|
||||||
|
pub fn deserialize_and_verify_metadata(
|
||||||
|
info: &Info,
|
||||||
|
derivation_data: SplTokenMetaDerivationData,
|
||||||
|
) -> Result<spl_token_metadata::state::Metadata> {
|
||||||
|
// Verify pda.
|
||||||
|
info.verify_derivation(&spl_token_metadata::id(), &derivation_data)?;
|
||||||
|
|
||||||
|
// There must be account data for token's metadata.
|
||||||
|
if info.data_is_empty() {
|
||||||
|
return Err(TokenBridgeError::NonexistentTokenMetadataAccount.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account must belong to Metaplex Token Metadata program.
|
||||||
|
if *info.owner != spl_token_metadata::id() {
|
||||||
|
return Err(TokenBridgeError::WrongAccountOwner.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account must be the expected Metadata length.
|
||||||
|
if info.data_len() != spl_token_metadata::state::MAX_METADATA_LEN {
|
||||||
|
return Err(TokenBridgeError::InvalidMetadata.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data: &[u8] = &info.data.borrow_mut();
|
||||||
|
|
||||||
|
// Unfortunately we cannot use `map_err` easily, so we will match certain deserialization conditions.
|
||||||
|
match spl_token_metadata::utils::meta_deser_unchecked(&mut data) {
|
||||||
|
Ok(deserialized) => {
|
||||||
|
if deserialized.key == MetadataV1 {
|
||||||
|
Ok(deserialized)
|
||||||
|
} else {
|
||||||
|
Err(TokenBridgeError::NotMetadataV1Account.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(TokenBridgeError::InvalidMetadata.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{
|
accounts::{
|
||||||
|
deserialize_and_verify_metadata,
|
||||||
ConfigAccount,
|
ConfigAccount,
|
||||||
CoreBridge,
|
CoreBridge,
|
||||||
EmitterAccount,
|
EmitterAccount,
|
||||||
|
@ -10,7 +11,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
messages::PayloadAssetMeta,
|
messages::PayloadAssetMeta,
|
||||||
types::*,
|
types::*,
|
||||||
TokenBridgeError::*,
|
|
||||||
};
|
};
|
||||||
use bridge::{
|
use bridge::{
|
||||||
api::PostMessageData,
|
api::PostMessageData,
|
||||||
|
@ -34,7 +34,6 @@ use solitaire::{
|
||||||
},
|
},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use spl_token_metadata::state::Metadata;
|
|
||||||
|
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
pub struct AttestToken<'b> {
|
pub struct AttestToken<'b> {
|
||||||
|
@ -118,16 +117,7 @@ pub fn attest_token(
|
||||||
|
|
||||||
// Assign metadata if an SPL Metadata account exists for the SPL token in question.
|
// Assign metadata if an SPL Metadata account exists for the SPL token in question.
|
||||||
if !accs.spl_metadata.data_is_empty() {
|
if !accs.spl_metadata.data_is_empty() {
|
||||||
let derivation_data: SplTokenMetaDerivationData = (&*accs).into();
|
let metadata = deserialize_and_verify_metadata(&accs.spl_metadata, (&*accs).into())?;
|
||||||
accs.spl_metadata
|
|
||||||
.verify_derivation(&spl_token_metadata::id(), &derivation_data)?;
|
|
||||||
|
|
||||||
if *accs.spl_metadata.owner != spl_token_metadata::id() {
|
|
||||||
return Err(WrongAccountOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let metadata: Metadata =
|
|
||||||
Metadata::from_account_info(accs.spl_metadata.info()).ok_or(InvalidMetadata)?;
|
|
||||||
payload.name = metadata.data.name.clone();
|
payload.name = metadata.data.name.clone();
|
||||||
payload.symbol = metadata.data.symbol;
|
payload.symbol = metadata.data.symbol;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{
|
accounts::{
|
||||||
|
deserialize_and_verify_metadata,
|
||||||
ConfigAccount,
|
ConfigAccount,
|
||||||
Endpoint,
|
Endpoint,
|
||||||
EndpointDerivationData,
|
EndpointDerivationData,
|
||||||
|
@ -14,7 +15,6 @@ use crate::{
|
||||||
messages::PayloadAssetMeta,
|
messages::PayloadAssetMeta,
|
||||||
TokenBridgeError::{
|
TokenBridgeError::{
|
||||||
InvalidChain,
|
InvalidChain,
|
||||||
InvalidMetadata,
|
|
||||||
InvalidVAA,
|
InvalidVAA,
|
||||||
},
|
},
|
||||||
INVALID_VAAS,
|
INVALID_VAAS,
|
||||||
|
@ -40,10 +40,6 @@ use solitaire::{
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use spl_token_metadata::state::{
|
|
||||||
Data as SplData,
|
|
||||||
Metadata,
|
|
||||||
};
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
#[derive(FromAccounts)]
|
#[derive(FromAccounts)]
|
||||||
|
@ -164,7 +160,7 @@ pub fn create_accounts(
|
||||||
let name = truncate_utf8(&accs.vaa.name, 32 - 11) + " (Wormhole)";
|
let name = truncate_utf8(&accs.vaa.name, 32 - 11) + " (Wormhole)";
|
||||||
let symbol = truncate_utf8(&accs.vaa.symbol, 10);
|
let symbol = truncate_utf8(&accs.vaa.symbol, 10);
|
||||||
|
|
||||||
let spl_token_metadata_ix = spl_token_metadata::instruction::create_metadata_accounts(
|
let spl_token_metadata_ix = spl_token_metadata::instruction::create_metadata_accounts_v3(
|
||||||
spl_token_metadata::id(),
|
spl_token_metadata::id(),
|
||||||
*accs.spl_metadata.key,
|
*accs.spl_metadata.key,
|
||||||
*accs.mint.info().key,
|
*accs.mint.info().key,
|
||||||
|
@ -178,6 +174,9 @@ pub fn create_accounts(
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
invoke_seeded(&spl_token_metadata_ix, ctx, &accs.mint_authority, None)?;
|
invoke_seeded(&spl_token_metadata_ix, ctx, &accs.mint_authority, None)?;
|
||||||
|
|
||||||
|
@ -194,28 +193,34 @@ pub fn update_accounts(
|
||||||
accs: &mut CreateWrapped,
|
accs: &mut CreateWrapped,
|
||||||
_data: CreateWrappedData,
|
_data: CreateWrappedData,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
accs.spl_metadata.verify_derivation(
|
// Checks in this method are redundant with what occurs in `update_metadata_accounts_v2`, but we want to make
|
||||||
&spl_token_metadata::id(),
|
// sure that the account we are deserializing is legitimate.
|
||||||
&SplTokenMetaDerivationData {
|
let metadata = deserialize_and_verify_metadata(
|
||||||
|
&accs.spl_metadata,
|
||||||
|
SplTokenMetaDerivationData {
|
||||||
mint: *accs.mint.info().key,
|
mint: *accs.mint.info().key,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut metadata: SplData = Metadata::from_account_info(accs.spl_metadata.info())
|
// Normalize token metadata's name and symbol.
|
||||||
.ok_or(InvalidMetadata)?
|
let new_data_v2 = spl_token_metadata::state::DataV2 {
|
||||||
.data;
|
name: truncate_utf8(&accs.vaa.name, 32 - 11) + " (Wormhole)",
|
||||||
|
symbol: truncate_utf8(&accs.vaa.symbol, 10),
|
||||||
// Normalize token metadata.
|
uri: metadata.data.uri,
|
||||||
metadata.name = truncate_utf8(&accs.vaa.name, 32 - 11) + " (Wormhole)";
|
seller_fee_basis_points: metadata.data.seller_fee_basis_points,
|
||||||
metadata.symbol = truncate_utf8(&accs.vaa.symbol, 10);
|
creators: metadata.data.creators,
|
||||||
|
collection: metadata.collection,
|
||||||
|
uses: metadata.uses,
|
||||||
|
};
|
||||||
|
|
||||||
// Update SPL Metadata
|
// Update SPL Metadata
|
||||||
let spl_token_metadata_ix = spl_token_metadata::instruction::update_metadata_accounts(
|
let spl_token_metadata_ix = spl_token_metadata::instruction::update_metadata_accounts_v2(
|
||||||
spl_token_metadata::id(),
|
spl_token_metadata::id(),
|
||||||
*accs.spl_metadata.key,
|
*accs.spl_metadata.key,
|
||||||
*accs.mint_authority.info().key,
|
*accs.mint_authority.info().key,
|
||||||
None,
|
None,
|
||||||
Some(metadata),
|
Some(new_data_v2),
|
||||||
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
invoke_seeded(&spl_token_metadata_ix, ctx, &accs.mint_authority, None)?;
|
invoke_seeded(&spl_token_metadata_ix, ctx, &accs.mint_authority, None)?;
|
||||||
|
|
|
@ -89,6 +89,8 @@ pub enum TokenBridgeError {
|
||||||
InvalidFee,
|
InvalidFee,
|
||||||
InvalidRecipient,
|
InvalidRecipient,
|
||||||
InvalidVAA,
|
InvalidVAA,
|
||||||
|
NonexistentTokenMetadataAccount,
|
||||||
|
NotMetadataV1Account,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TokenBridgeError> for SolitaireError {
|
impl From<TokenBridgeError> for SolitaireError {
|
||||||
|
|
|
@ -131,7 +131,7 @@ mod helpers {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut builder = ProgramTest::new("bridge", program, processor!(bridge::solitaire));
|
let mut builder = ProgramTest::new("bridge", program, processor!(bridge::solitaire));
|
||||||
builder.add_program("spl_token_metadata", spl_token_metadata::id(), None);
|
builder.add_program("mpl_token_metadata", spl_token_metadata::id(), None);
|
||||||
builder.add_program(
|
builder.add_program(
|
||||||
"token_bridge",
|
"token_bridge",
|
||||||
token_program,
|
token_program,
|
||||||
|
@ -618,21 +618,26 @@ mod helpers {
|
||||||
client,
|
client,
|
||||||
payer,
|
payer,
|
||||||
&[payer, mint_authority],
|
&[payer, mint_authority],
|
||||||
&[spl_token_metadata::instruction::create_metadata_accounts(
|
&[
|
||||||
spl_token_metadata::id(),
|
spl_token_metadata::instruction::create_metadata_accounts_v3(
|
||||||
metadata_account,
|
spl_token_metadata::id(),
|
||||||
mint.pubkey(),
|
metadata_account,
|
||||||
mint_authority.pubkey(),
|
mint.pubkey(),
|
||||||
payer.pubkey(),
|
mint_authority.pubkey(),
|
||||||
update_authority,
|
payer.pubkey(),
|
||||||
name,
|
update_authority,
|
||||||
symbol,
|
name,
|
||||||
"https://token.org".to_string(),
|
symbol,
|
||||||
None,
|
"https://token.org".to_string(),
|
||||||
0,
|
None,
|
||||||
false,
|
0,
|
||||||
false,
|
false,
|
||||||
)],
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
],
|
||||||
CommitmentLevel::Processed,
|
CommitmentLevel::Processed,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "spl-token-metadata"
|
|
||||||
version = "0.0.1"
|
|
||||||
description = "Metaplex Metadata"
|
|
||||||
authors = ["Metaplex Maintainers <maintainers@metaplex.com>"]
|
|
||||||
repository = "https://github.com/metaplex-foundation/metaplex"
|
|
||||||
license = "Apache-2.0"
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib", "lib"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
no-entrypoint = []
|
|
||||||
test-bpf = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
borsh = "=0.9.3"
|
|
||||||
solana-program = "=1.10.31"
|
|
||||||
spl-token = { version = "=3.3.0", features = ["no-entrypoint"] }
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: Token Metadata Program
|
|
||||||
---
|
|
||||||
|
|
||||||
Fork of the SPL Token Metadata program from the Metaplex repository, this is
|
|
||||||
temporary until there are versioned releases we can compile against. Currently
|
|
||||||
the upstream version depends on a version of solana with conflicting versions
|
|
||||||
of borsh.
|
|
|
@ -1,2 +0,0 @@
|
||||||
[target.bpfel-unknown-unknown.dependencies.std]
|
|
||||||
features = []
|
|
Binary file not shown.
|
@ -1,138 +0,0 @@
|
||||||
use crate::state::{
|
|
||||||
Creator,
|
|
||||||
Data,
|
|
||||||
EDITION,
|
|
||||||
EDITION_MARKER_BIT_SIZE,
|
|
||||||
PREFIX,
|
|
||||||
};
|
|
||||||
use borsh::{
|
|
||||||
BorshDeserialize,
|
|
||||||
BorshSerialize,
|
|
||||||
};
|
|
||||||
use solana_program::{
|
|
||||||
instruction::{
|
|
||||||
AccountMeta,
|
|
||||||
Instruction,
|
|
||||||
},
|
|
||||||
pubkey::Pubkey,
|
|
||||||
sysvar,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
|
|
||||||
/// Args for update call
|
|
||||||
pub struct UpdateMetadataAccountArgs {
|
|
||||||
pub data: Option<Data>,
|
|
||||||
pub update_authority: Option<Pubkey>,
|
|
||||||
pub primary_sale_happened: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
|
|
||||||
/// Args for create call
|
|
||||||
pub struct CreateMetadataAccountArgs {
|
|
||||||
/// Note that unique metadatas are disabled for now.
|
|
||||||
pub data: Data,
|
|
||||||
/// Whether you want your metadata to be updateable in the future.
|
|
||||||
pub is_mutable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
|
|
||||||
pub struct CreateMasterEditionArgs {
|
|
||||||
/// If set, means that no more than this number of editions can ever be minted. This is immutable.
|
|
||||||
pub max_supply: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
|
|
||||||
pub struct MintNewEditionFromMasterEditionViaTokenArgs {
|
|
||||||
pub edition: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Instructions supported by the Metadata program.
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, Clone)]
|
|
||||||
pub enum MetadataInstruction {
|
|
||||||
/// Create Metadata object.
|
|
||||||
/// 0. `[writable]` Metadata key (pda of ['metadata', program id, mint id])
|
|
||||||
/// 1. `[]` Mint of token asset
|
|
||||||
/// 2. `[signer]` Mint authority
|
|
||||||
/// 3. `[signer]` payer
|
|
||||||
/// 4. `[]` update authority info
|
|
||||||
/// 5. `[]` System program
|
|
||||||
/// 6. `[]` Rent info
|
|
||||||
CreateMetadataAccount(CreateMetadataAccountArgs),
|
|
||||||
|
|
||||||
/// Update a Metadata
|
|
||||||
/// 0. `[writable]` Metadata account
|
|
||||||
/// 1. `[signer]` Update authority key
|
|
||||||
UpdateMetadataAccount(UpdateMetadataAccountArgs),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an CreateMetadataAccounts instruction
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn create_metadata_accounts(
|
|
||||||
program_id: Pubkey,
|
|
||||||
metadata_account: Pubkey,
|
|
||||||
mint: Pubkey,
|
|
||||||
mint_authority: Pubkey,
|
|
||||||
payer: Pubkey,
|
|
||||||
update_authority: Pubkey,
|
|
||||||
name: String,
|
|
||||||
symbol: String,
|
|
||||||
uri: String,
|
|
||||||
creators: Option<Vec<Creator>>,
|
|
||||||
seller_fee_basis_points: u16,
|
|
||||||
update_authority_is_signer: bool,
|
|
||||||
is_mutable: bool,
|
|
||||||
) -> Instruction {
|
|
||||||
Instruction {
|
|
||||||
program_id,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(metadata_account, false),
|
|
||||||
AccountMeta::new_readonly(mint, false),
|
|
||||||
AccountMeta::new_readonly(mint_authority, true),
|
|
||||||
AccountMeta::new(payer, true),
|
|
||||||
AccountMeta::new_readonly(update_authority, update_authority_is_signer),
|
|
||||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
|
||||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
|
||||||
],
|
|
||||||
data: MetadataInstruction::CreateMetadataAccount(CreateMetadataAccountArgs {
|
|
||||||
data: Data {
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
uri,
|
|
||||||
seller_fee_basis_points,
|
|
||||||
creators,
|
|
||||||
},
|
|
||||||
is_mutable,
|
|
||||||
})
|
|
||||||
.try_to_vec()
|
|
||||||
.unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// update metadata account instruction
|
|
||||||
pub fn update_metadata_accounts(
|
|
||||||
program_id: Pubkey,
|
|
||||||
metadata_account: Pubkey,
|
|
||||||
update_authority: Pubkey,
|
|
||||||
new_update_authority: Option<Pubkey>,
|
|
||||||
data: Option<Data>,
|
|
||||||
primary_sale_happened: Option<bool>,
|
|
||||||
) -> Instruction {
|
|
||||||
Instruction {
|
|
||||||
program_id,
|
|
||||||
accounts: vec![
|
|
||||||
AccountMeta::new(metadata_account, false),
|
|
||||||
AccountMeta::new_readonly(update_authority, true),
|
|
||||||
],
|
|
||||||
data: MetadataInstruction::UpdateMetadataAccount(UpdateMetadataAccountArgs {
|
|
||||||
data,
|
|
||||||
update_authority: new_update_authority,
|
|
||||||
primary_sale_happened,
|
|
||||||
})
|
|
||||||
.try_to_vec()
|
|
||||||
.unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// The solana_program::declare_id! macro generates spurious import statements.
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub mod instruction;
|
|
||||||
pub mod state;
|
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
solana_program::declare_id!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
|
|
@ -1,124 +0,0 @@
|
||||||
use crate::utils::try_from_slice_checked;
|
|
||||||
use borsh::{
|
|
||||||
BorshDeserialize,
|
|
||||||
BorshSerialize,
|
|
||||||
};
|
|
||||||
use solana_program::{
|
|
||||||
account_info::AccountInfo,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// prefix used for PDAs to avoid certain collision attacks (https://en.wikipedia.org/wiki/Collision_attack#Chosen-prefix_collision_attack)
|
|
||||||
pub const PREFIX: &str = "metadata";
|
|
||||||
|
|
||||||
/// Used in seeds to make Edition model pda address
|
|
||||||
pub const EDITION: &str = "edition";
|
|
||||||
|
|
||||||
pub const RESERVATION: &str = "reservation";
|
|
||||||
|
|
||||||
pub const MAX_NAME_LENGTH: usize = 32;
|
|
||||||
|
|
||||||
pub const MAX_SYMBOL_LENGTH: usize = 10;
|
|
||||||
|
|
||||||
pub const MAX_URI_LENGTH: usize = 200;
|
|
||||||
|
|
||||||
pub const MAX_METADATA_LEN: usize = 1
|
|
||||||
+ 32
|
|
||||||
+ 32
|
|
||||||
+ MAX_NAME_LENGTH
|
|
||||||
+ MAX_SYMBOL_LENGTH
|
|
||||||
+ MAX_URI_LENGTH
|
|
||||||
+ MAX_CREATOR_LIMIT * MAX_CREATOR_LEN
|
|
||||||
+ 2
|
|
||||||
+ 1
|
|
||||||
+ 1
|
|
||||||
+ 198;
|
|
||||||
|
|
||||||
pub const MAX_EDITION_LEN: usize = 1 + 32 + 8 + 200;
|
|
||||||
|
|
||||||
// Large buffer because the older master editions have two pubkeys in them,
|
|
||||||
// need to keep two versions same size because the conversion process actually changes the same account
|
|
||||||
// by rewriting it.
|
|
||||||
pub const MAX_MASTER_EDITION_LEN: usize = 1 + 9 + 8 + 264;
|
|
||||||
|
|
||||||
pub const MAX_CREATOR_LIMIT: usize = 5;
|
|
||||||
|
|
||||||
pub const MAX_CREATOR_LEN: usize = 32 + 1 + 1;
|
|
||||||
|
|
||||||
pub const MAX_RESERVATIONS: usize = 200;
|
|
||||||
|
|
||||||
// can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec
|
|
||||||
pub const MAX_RESERVATION_LIST_V1_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 34 + 100;
|
|
||||||
|
|
||||||
// can hold up to 200 keys per reservation, note: the extra 8 is for number of elements in the vec
|
|
||||||
pub const MAX_RESERVATION_LIST_SIZE: usize = 1 + 32 + 8 + 8 + MAX_RESERVATIONS * 48 + 8 + 8 + 84;
|
|
||||||
|
|
||||||
pub const MAX_EDITION_MARKER_SIZE: usize = 32;
|
|
||||||
|
|
||||||
pub const EDITION_MARKER_BIT_SIZE: u64 = 248;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone, Copy)]
|
|
||||||
pub enum Key {
|
|
||||||
Uninitialized,
|
|
||||||
EditionV1,
|
|
||||||
MasterEditionV1,
|
|
||||||
ReservationListV1,
|
|
||||||
MetadataV1,
|
|
||||||
ReservationListV2,
|
|
||||||
MasterEditionV2,
|
|
||||||
EditionMarker,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Key {
|
|
||||||
fn default() -> Self {
|
|
||||||
Key::Uninitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, Default, PartialEq, Debug, Clone)]
|
|
||||||
pub struct Data {
|
|
||||||
/// The name of the asset
|
|
||||||
pub name: String,
|
|
||||||
/// The symbol for the asset
|
|
||||||
pub symbol: String,
|
|
||||||
/// URI pointing to JSON representing the asset
|
|
||||||
pub uri: String,
|
|
||||||
/// Royalty basis points that goes to creators in secondary sales (0-10000)
|
|
||||||
pub seller_fee_basis_points: u16,
|
|
||||||
/// Array of creators, optional
|
|
||||||
pub creators: Option<Vec<Creator>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Default)]
|
|
||||||
pub struct Metadata {
|
|
||||||
pub key: Key,
|
|
||||||
pub update_authority: Pubkey,
|
|
||||||
pub mint: Pubkey,
|
|
||||||
pub data: Data,
|
|
||||||
// Immutable, once flipped, all sales of this metadata are considered secondary.
|
|
||||||
pub primary_sale_happened: bool,
|
|
||||||
// Whether or not the data struct is mutable, default is not
|
|
||||||
pub is_mutable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Metadata {
|
|
||||||
pub fn from_bytes(a: &[u8]) -> Option<Metadata> {
|
|
||||||
try_from_slice_checked(a, Key::MetadataV1, MAX_METADATA_LEN)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_account_info(a: &AccountInfo) -> Option<Metadata> {
|
|
||||||
try_from_slice_checked(&a.data.borrow_mut(), Key::MetadataV1, MAX_METADATA_LEN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
|
|
||||||
pub struct Creator {
|
|
||||||
pub address: Pubkey,
|
|
||||||
pub verified: bool,
|
|
||||||
// In percentages, NOT basis points ;) Watch out!
|
|
||||||
pub share: u8,
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use crate::state::Key;
|
|
||||||
use borsh::BorshDeserialize;
|
|
||||||
use solana_program::borsh::try_from_slice_unchecked;
|
|
||||||
|
|
||||||
pub fn try_from_slice_checked<T: BorshDeserialize>(
|
|
||||||
data: &[u8],
|
|
||||||
data_type: Key,
|
|
||||||
data_size: usize,
|
|
||||||
) -> Option<T> {
|
|
||||||
if (data[0] != data_type as u8 && data[0] != Key::Uninitialized as u8)
|
|
||||||
|| data.len() != data_size
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
try_from_slice_unchecked(data).ok()
|
|
||||||
}
|
|
|
@ -1,3 +1,5 @@
|
||||||
.test
|
.test
|
||||||
artifacts
|
artifacts*
|
||||||
node_modules
|
node_modules
|
||||||
|
wormhole-main
|
||||||
|
validator.log
|
||||||
|
|
|
@ -7,15 +7,33 @@
|
||||||
node_modules:
|
node_modules:
|
||||||
yarn
|
yarn
|
||||||
|
|
||||||
artifacts: node_modules
|
artifacts:
|
||||||
cd ../../solana && DOCKER_BUILDKIT=1 docker build -f Dockerfile --build-arg BRIDGE_ADDRESS=agnnozV7x6ffAhi8xVhBd5dShfLnuUKKPEMX1tJ1nDC -o ../testing/solana-test-validator/artifacts .
|
cd ../../solana && \
|
||||||
|
DOCKER_BUILDKIT=1 docker build \
|
||||||
|
-f Dockerfile \
|
||||||
|
--build-arg BRIDGE_ADDRESS=agnnozV7x6ffAhi8xVhBd5dShfLnuUKKPEMX1tJ1nDC \
|
||||||
|
-o ../testing/solana-test-validator/artifacts .
|
||||||
|
|
||||||
|
artifacts-main:
|
||||||
|
git clone \
|
||||||
|
--depth 1 \
|
||||||
|
--branch main \
|
||||||
|
--filter=blob:none \
|
||||||
|
https://github.com/wormhole-foundation/wormhole \
|
||||||
|
wormhole-main
|
||||||
|
cd wormhole-main/solana && \
|
||||||
|
DOCKER_BUILDKIT=1 docker build \
|
||||||
|
-f Dockerfile \
|
||||||
|
--build-arg BRIDGE_ADDRESS=agnnozV7x6ffAhi8xVhBd5dShfLnuUKKPEMX1tJ1nDC \
|
||||||
|
-o ../../artifacts-main .
|
||||||
|
rm -rf wormhole-main
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: artifacts
|
test: node_modules artifacts-main artifacts
|
||||||
@echo "Running integration tests"
|
@echo "Running integration tests"
|
||||||
yarn run sdk-tests
|
yarn run sdk-tests
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -rf artifacts node_modules validator.log .test
|
rm -rf artifacts artifacts-main wormhole-main node_modules validator.log .test
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
|
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@metaplex-foundation/mpl-token-metadata": "^2.11.1",
|
||||||
"@project-serum/anchor": "0.25.0",
|
"@project-serum/anchor": "0.25.0",
|
||||||
"@solana/spl-token": "^0.3.1",
|
"@solana/spl-token": "^0.3.1",
|
||||||
"@solana/web3.js": "^1.53.0",
|
"@solana/web3.js": "^1.53.0",
|
||||||
|
|
|
@ -15,9 +15,10 @@ ACCOUNTS=$ROOT/sdk-tests/accounts
|
||||||
TEST=$ROOT/.test
|
TEST=$ROOT/.test
|
||||||
|
|
||||||
solana-test-validator --reset \
|
solana-test-validator --reset \
|
||||||
--bpf-program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s $ARTIFACTS/spl_token_metadata.so \
|
--bpf-program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s $ARTIFACTS/mpl_token_metadata.so \
|
||||||
|
--account-dir $ACCOUNTS \
|
||||||
--ledger $TEST > validator.log 2>&1 &
|
--ledger $TEST > validator.log 2>&1 &
|
||||||
sleep 2
|
sleep 5
|
||||||
|
|
||||||
### write program logs
|
### write program logs
|
||||||
PROGRAM_LOGS=$TEST/program-logs
|
PROGRAM_LOGS=$TEST/program-logs
|
||||||
|
|
|
@ -69,7 +69,7 @@ describe("Deploy and Upgrade Programs", () => {
|
||||||
|
|
||||||
describe("Wormhole (Core Bridge)", () => {
|
describe("Wormhole (Core Bridge)", () => {
|
||||||
it("Deploy and Initialize", async () => {
|
it("Deploy and Initialize", async () => {
|
||||||
const artifactPath = `${__dirname}/../artifacts/bridge.so`;
|
const artifactPath = `${__dirname}/../artifacts-main/bridge.so`;
|
||||||
const programIdPath = `${__dirname}/keys/${CORE_BRIDGE_ADDRESS}.json`;
|
const programIdPath = `${__dirname}/keys/${CORE_BRIDGE_ADDRESS}.json`;
|
||||||
const upgradeAuthority = deriveUpgradeAuthorityKey(CORE_BRIDGE_ADDRESS);
|
const upgradeAuthority = deriveUpgradeAuthorityKey(CORE_BRIDGE_ADDRESS);
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ describe("Deploy and Upgrade Programs", () => {
|
||||||
|
|
||||||
describe("Token Bridge", () => {
|
describe("Token Bridge", () => {
|
||||||
it("Deploy and Initialize", async () => {
|
it("Deploy and Initialize", async () => {
|
||||||
const artifactPath = `${__dirname}/../artifacts/token_bridge.so`;
|
const artifactPath = `${__dirname}/../artifacts-main/token_bridge.so`;
|
||||||
const programIdPath = `${__dirname}/keys/${TOKEN_BRIDGE_ADDRESS}.json`;
|
const programIdPath = `${__dirname}/keys/${TOKEN_BRIDGE_ADDRESS}.json`;
|
||||||
const upgradeAuthority = deriveUpgradeAuthorityKey(TOKEN_BRIDGE_ADDRESS);
|
const upgradeAuthority = deriveUpgradeAuthorityKey(TOKEN_BRIDGE_ADDRESS);
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ describe("Deploy and Upgrade Programs", () => {
|
||||||
|
|
||||||
describe("NFT Bridge", () => {
|
describe("NFT Bridge", () => {
|
||||||
it("Deploy and Initialize", async () => {
|
it("Deploy and Initialize", async () => {
|
||||||
const artifactPath = `${__dirname}/../artifacts/nft_bridge.so`;
|
const artifactPath = `${__dirname}/../artifacts-main/nft_bridge.so`;
|
||||||
const programIdPath = `${__dirname}/keys/${NFT_BRIDGE_ADDRESS}.json`;
|
const programIdPath = `${__dirname}/keys/${NFT_BRIDGE_ADDRESS}.json`;
|
||||||
const upgradeAuthority = deriveUpgradeAuthorityKey(NFT_BRIDGE_ADDRESS);
|
const upgradeAuthority = deriveUpgradeAuthorityKey(NFT_BRIDGE_ADDRESS);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import * as web3 from "@solana/web3.js";
|
import * as web3 from "@solana/web3.js";
|
||||||
|
import {
|
||||||
|
Metadata,
|
||||||
|
PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID,
|
||||||
|
} from "@metaplex-foundation/mpl-token-metadata";
|
||||||
import {
|
import {
|
||||||
createMint,
|
createMint,
|
||||||
getAccount,
|
getAccount,
|
||||||
|
@ -30,6 +34,7 @@ import {
|
||||||
deriveEndpointKey,
|
deriveEndpointKey,
|
||||||
deriveMintAuthorityKey,
|
deriveMintAuthorityKey,
|
||||||
deriveRedeemerAccountKey,
|
deriveRedeemerAccountKey,
|
||||||
|
deriveTokenMetadataKey,
|
||||||
deriveWrappedMintKey,
|
deriveWrappedMintKey,
|
||||||
getAttestTokenAccounts,
|
getAttestTokenAccounts,
|
||||||
getCompleteTransferNativeAccounts,
|
getCompleteTransferNativeAccounts,
|
||||||
|
@ -54,7 +59,6 @@ import {
|
||||||
getTransferWrappedWithPayloadCpiAccounts,
|
getTransferWrappedWithPayloadCpiAccounts,
|
||||||
NodeWallet,
|
NodeWallet,
|
||||||
signSendAndConfirmTransaction,
|
signSendAndConfirmTransaction,
|
||||||
SplTokenMetadataProgram,
|
|
||||||
} from "../../../sdk/js/src/solana";
|
} from "../../../sdk/js/src/solana";
|
||||||
import {
|
import {
|
||||||
deriveWormholeEmitterKey,
|
deriveWormholeEmitterKey,
|
||||||
|
@ -80,6 +84,9 @@ import {
|
||||||
GUARDIAN_SET_INDEX,
|
GUARDIAN_SET_INDEX,
|
||||||
LOCALHOST,
|
LOCALHOST,
|
||||||
WETH_ADDRESS,
|
WETH_ADDRESS,
|
||||||
|
DEADBEEF_ADDRESS,
|
||||||
|
DEADBEEF_METADATA_ADDRESS,
|
||||||
|
DEADBEEF_MINT_ADDRESS,
|
||||||
} from "./helpers/consts";
|
} from "./helpers/consts";
|
||||||
import { ethAddressToBuffer, now } from "./helpers/utils";
|
import { ethAddressToBuffer, now } from "./helpers/utils";
|
||||||
import {
|
import {
|
||||||
|
@ -517,7 +524,7 @@ describe("Token Bridge", () => {
|
||||||
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
|
"4CgrjMnDneBjBBEyXtcikLTbAWpHAD1cwn8W1sSSCLru"
|
||||||
);
|
);
|
||||||
expect(accounts.vaa.toString()).to.equal(
|
expect(accounts.vaa.toString()).to.equal(
|
||||||
"AaZqjqvg8QirKetuR799Smfw5gyAWobUooGsvxzr1aoX"
|
"4NDyWDtRvfEdi48a9JgYG28m919hrcdW8gNgRg3jwU99"
|
||||||
);
|
);
|
||||||
expect(accounts.claim.toString()).to.equal(
|
expect(accounts.claim.toString()).to.equal(
|
||||||
"4dyk94hhqektDX9wUBCL1ZkyQC1Xn3QaTSAdJeZzbTcJ"
|
"4dyk94hhqektDX9wUBCL1ZkyQC1Xn3QaTSAdJeZzbTcJ"
|
||||||
|
@ -538,9 +545,8 @@ describe("Token Bridge", () => {
|
||||||
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
||||||
.true;
|
.true;
|
||||||
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
||||||
expect(
|
expect(accounts.splMetadataProgram.equals(TOKEN_METADATA_PROGRAM_ID)).is
|
||||||
accounts.splMetadataProgram.equals(SplTokenMetadataProgram.programId)
|
.true;
|
||||||
).is.true;
|
|
||||||
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1552,6 +1558,278 @@ describe("Token Bridge", () => {
|
||||||
Buffer.compare(wrappedMeta.tokenAddress, expectedTokenAddress)
|
Buffer.compare(wrappedMeta.tokenAddress, expectedTokenAddress)
|
||||||
).to.equal(0);
|
).to.equal(0);
|
||||||
expect(wrappedMeta.originalDecimals).to.equal(decimals);
|
expect(wrappedMeta.originalDecimals).to.equal(decimals);
|
||||||
|
|
||||||
|
// check metadata
|
||||||
|
const expectedName = `${name} (Wormhole)`.padEnd(32, "\0");
|
||||||
|
const metadata = await Metadata.fromAccountAddress(
|
||||||
|
connection,
|
||||||
|
deriveTokenMetadataKey(mint)
|
||||||
|
);
|
||||||
|
expect(metadata.data.symbol.toString()).equals(symbol.padEnd(10, "\0"));
|
||||||
|
expect(metadata.data.name.toString()).equals(expectedName);
|
||||||
|
localVariables.oldName = expectedName;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Update (Create) Wrapped with New Metadata", async () => {
|
||||||
|
const tokenAddress = WETH_ADDRESS;
|
||||||
|
const oldName: string = localVariables.oldName;
|
||||||
|
|
||||||
|
const mint = deriveWrappedMintKey(
|
||||||
|
TOKEN_BRIDGE_ADDRESS,
|
||||||
|
ethereumTokenBridge.chain,
|
||||||
|
tokenAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
// check existing metadata
|
||||||
|
{
|
||||||
|
const metadata = await Metadata.fromAccountAddress(
|
||||||
|
connection,
|
||||||
|
deriveTokenMetadataKey(mint)
|
||||||
|
);
|
||||||
|
expect(metadata.data.name.toString()).equals(oldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decimals = 18;
|
||||||
|
const symbol = "WETH";
|
||||||
|
const name = "Wrapped Ether";
|
||||||
|
const nonce = 420;
|
||||||
|
const message = ethereumTokenBridge.publishAttestMeta(
|
||||||
|
tokenAddress,
|
||||||
|
decimals,
|
||||||
|
symbol,
|
||||||
|
name,
|
||||||
|
nonce
|
||||||
|
);
|
||||||
|
const signedVaa = guardians.addSignatures(
|
||||||
|
message,
|
||||||
|
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
|
||||||
|
);
|
||||||
|
|
||||||
|
const txSignatures = await postVaa(
|
||||||
|
connection,
|
||||||
|
wallet.signTransaction,
|
||||||
|
CORE_BRIDGE_ADDRESS,
|
||||||
|
wallet.key(),
|
||||||
|
signedVaa
|
||||||
|
).then((results) => results.map((result) => result.signature));
|
||||||
|
const postTx = txSignatures.pop()!;
|
||||||
|
for (const verifyTx of txSignatures) {
|
||||||
|
// console.log(`verifySignatures: ${verifyTx}`);
|
||||||
|
}
|
||||||
|
// console.log(`postVaa: ${postTx}`);
|
||||||
|
|
||||||
|
const createWrappedIx = createCreateWrappedInstruction(
|
||||||
|
TOKEN_BRIDGE_ADDRESS,
|
||||||
|
CORE_BRIDGE_ADDRESS,
|
||||||
|
wallet.key(),
|
||||||
|
signedVaa
|
||||||
|
);
|
||||||
|
|
||||||
|
const createWrappedTx = await web3.sendAndConfirmTransaction(
|
||||||
|
connection,
|
||||||
|
new web3.Transaction().add(createWrappedIx),
|
||||||
|
[wallet.signer()]
|
||||||
|
);
|
||||||
|
// console.log(`createWrappedTx: ${createWrappedTx}`);
|
||||||
|
|
||||||
|
// verify data
|
||||||
|
const parsed = parseAttestMetaVaa(signedVaa);
|
||||||
|
const messageData = await getPostedVaa(
|
||||||
|
connection,
|
||||||
|
CORE_BRIDGE_ADDRESS,
|
||||||
|
parsed.hash
|
||||||
|
).then((posted) => posted.message);
|
||||||
|
|
||||||
|
expect(messageData.consistencyLevel).to.equal(
|
||||||
|
ethereumTokenBridge.consistencyLevel
|
||||||
|
);
|
||||||
|
const expectedEmitter = ethAddressToBuffer(
|
||||||
|
ETHEREUM_TOKEN_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
Buffer.compare(messageData.emitterAddress, expectedEmitter)
|
||||||
|
).to.equal(0);
|
||||||
|
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
|
||||||
|
expect(messageData.nonce).to.equal(nonce);
|
||||||
|
expect(messageData.sequence).to.equal(3n);
|
||||||
|
expect(messageData.vaaTime).to.equal(0);
|
||||||
|
expect(messageData.vaaVersion).to.equal(1);
|
||||||
|
expect(Buffer.compare(parsed.payload, messageData.payload)).to.equal(0);
|
||||||
|
|
||||||
|
const assetMeta = parseAttestMetaPayload(messageData.payload);
|
||||||
|
expect(assetMeta.payloadType).to.equal(2);
|
||||||
|
const expectedTokenAddress = ethAddressToBuffer(tokenAddress);
|
||||||
|
expect(
|
||||||
|
Buffer.compare(assetMeta.tokenAddress, expectedTokenAddress)
|
||||||
|
).to.equal(0);
|
||||||
|
expect(assetMeta.tokenChain).to.equal(ethereumTokenBridge.chain);
|
||||||
|
expect(assetMeta.decimals).to.equal(decimals);
|
||||||
|
expect(assetMeta.symbol).to.equal(symbol);
|
||||||
|
expect(assetMeta.name).to.equal(name);
|
||||||
|
|
||||||
|
// check wrapped mint
|
||||||
|
const mintInfo = await getMint(connection, mint);
|
||||||
|
expect(mintInfo.decimals).to.equal(8);
|
||||||
|
expect(mintInfo.mintAuthority).is.not.null;
|
||||||
|
expect(
|
||||||
|
mintInfo.mintAuthority?.equals(
|
||||||
|
deriveMintAuthorityKey(TOKEN_BRIDGE_ADDRESS)
|
||||||
|
)
|
||||||
|
).is.true;
|
||||||
|
expect(mintInfo.supply).to.equal(0n);
|
||||||
|
|
||||||
|
// check wrapped meta
|
||||||
|
const wrappedMeta = await getWrappedMeta(
|
||||||
|
connection,
|
||||||
|
TOKEN_BRIDGE_ADDRESS,
|
||||||
|
mint
|
||||||
|
);
|
||||||
|
expect(wrappedMeta.chain).to.equal(ethereumTokenBridge.chain);
|
||||||
|
expect(
|
||||||
|
Buffer.compare(wrappedMeta.tokenAddress, expectedTokenAddress)
|
||||||
|
).to.equal(0);
|
||||||
|
expect(wrappedMeta.originalDecimals).to.equal(decimals);
|
||||||
|
|
||||||
|
// check metadata
|
||||||
|
const metadata = await Metadata.fromAccountAddress(
|
||||||
|
connection,
|
||||||
|
deriveTokenMetadataKey(mint)
|
||||||
|
);
|
||||||
|
expect(metadata.data.name.toString()).not.equals(oldName);
|
||||||
|
|
||||||
|
expect(metadata.data.symbol.toString()).equals(symbol.padEnd(10, "\0"));
|
||||||
|
expect(metadata.data.name.toString()).equals(
|
||||||
|
`${name} (Wormhole)`.padEnd(32, "\0")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Update (Create) Wrapped with New Metadata for V1 Metadata Account", async () => {
|
||||||
|
const tokenAddress = DEADBEEF_ADDRESS;
|
||||||
|
const oldExpectedName = "Dead Beef (Wormhole)".padEnd(32, "\0");
|
||||||
|
|
||||||
|
// fetch previously created metadata account
|
||||||
|
// check wrapped mint
|
||||||
|
{
|
||||||
|
const mint = deriveWrappedMintKey(
|
||||||
|
TOKEN_BRIDGE_ADDRESS,
|
||||||
|
ethereumTokenBridge.chain,
|
||||||
|
tokenAddress
|
||||||
|
);
|
||||||
|
expect(mint.toString()).equals(DEADBEEF_MINT_ADDRESS);
|
||||||
|
|
||||||
|
const metadataKey = deriveTokenMetadataKey(mint);
|
||||||
|
expect(metadataKey.toString()).equals(DEADBEEF_METADATA_ADDRESS);
|
||||||
|
|
||||||
|
const metadata = await Metadata.fromAccountAddress(
|
||||||
|
connection,
|
||||||
|
metadataKey
|
||||||
|
);
|
||||||
|
expect(metadata.data.name.toString()).equals(oldExpectedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decimals = 18;
|
||||||
|
const symbol = "BEEF";
|
||||||
|
const name = "Dead Beef Modified";
|
||||||
|
const nonce = 420;
|
||||||
|
const message = ethereumTokenBridge.publishAttestMeta(
|
||||||
|
tokenAddress,
|
||||||
|
decimals,
|
||||||
|
symbol,
|
||||||
|
name,
|
||||||
|
nonce
|
||||||
|
);
|
||||||
|
const signedVaa = guardians.addSignatures(
|
||||||
|
message,
|
||||||
|
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
|
||||||
|
);
|
||||||
|
|
||||||
|
const txSignatures = await postVaa(
|
||||||
|
connection,
|
||||||
|
wallet.signTransaction,
|
||||||
|
CORE_BRIDGE_ADDRESS,
|
||||||
|
wallet.key(),
|
||||||
|
signedVaa
|
||||||
|
).then((results) => results.map((result) => result.signature));
|
||||||
|
const postTx = txSignatures.pop()!;
|
||||||
|
for (const verifyTx of txSignatures) {
|
||||||
|
// console.log(`verifySignatures: ${verifyTx}`);
|
||||||
|
}
|
||||||
|
// console.log(`postVaa: ${postTx}`);
|
||||||
|
|
||||||
|
const createWrappedIx = createCreateWrappedInstruction(
|
||||||
|
TOKEN_BRIDGE_ADDRESS,
|
||||||
|
CORE_BRIDGE_ADDRESS,
|
||||||
|
wallet.key(),
|
||||||
|
signedVaa
|
||||||
|
);
|
||||||
|
|
||||||
|
const createWrappedTx = await web3.sendAndConfirmTransaction(
|
||||||
|
connection,
|
||||||
|
new web3.Transaction().add(createWrappedIx),
|
||||||
|
[wallet.signer()]
|
||||||
|
);
|
||||||
|
// console.log(`createWrappedTx: ${createWrappedTx}`);
|
||||||
|
|
||||||
|
// verify data
|
||||||
|
const parsed = parseAttestMetaVaa(signedVaa);
|
||||||
|
const messageData = await getPostedVaa(
|
||||||
|
connection,
|
||||||
|
CORE_BRIDGE_ADDRESS,
|
||||||
|
parsed.hash
|
||||||
|
).then((posted) => posted.message);
|
||||||
|
|
||||||
|
expect(messageData.consistencyLevel).to.equal(
|
||||||
|
ethereumTokenBridge.consistencyLevel
|
||||||
|
);
|
||||||
|
const expectedEmitter = ethAddressToBuffer(
|
||||||
|
ETHEREUM_TOKEN_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
Buffer.compare(messageData.emitterAddress, expectedEmitter)
|
||||||
|
).to.equal(0);
|
||||||
|
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
|
||||||
|
expect(messageData.nonce).to.equal(nonce);
|
||||||
|
expect(messageData.sequence).to.equal(4n);
|
||||||
|
expect(messageData.vaaTime).to.equal(0);
|
||||||
|
expect(messageData.vaaVersion).to.equal(1);
|
||||||
|
expect(Buffer.compare(parsed.payload, messageData.payload)).to.equal(0);
|
||||||
|
|
||||||
|
const assetMeta = parseAttestMetaPayload(messageData.payload);
|
||||||
|
expect(assetMeta.payloadType).to.equal(2);
|
||||||
|
const expectedTokenAddress = ethAddressToBuffer(tokenAddress);
|
||||||
|
expect(
|
||||||
|
Buffer.compare(assetMeta.tokenAddress, expectedTokenAddress)
|
||||||
|
).to.equal(0);
|
||||||
|
expect(assetMeta.tokenChain).to.equal(ethereumTokenBridge.chain);
|
||||||
|
expect(assetMeta.decimals).to.equal(decimals);
|
||||||
|
expect(assetMeta.symbol).to.equal(symbol);
|
||||||
|
expect(assetMeta.name).to.equal(name);
|
||||||
|
|
||||||
|
// check wrapped mint
|
||||||
|
const mint = deriveWrappedMintKey(
|
||||||
|
TOKEN_BRIDGE_ADDRESS,
|
||||||
|
assetMeta.tokenChain,
|
||||||
|
assetMeta.tokenAddress
|
||||||
|
);
|
||||||
|
const mintInfo = await getMint(connection, mint);
|
||||||
|
expect(mintInfo.decimals).to.equal(8);
|
||||||
|
expect(mintInfo.mintAuthority).is.not.null;
|
||||||
|
expect(
|
||||||
|
mintInfo.mintAuthority?.equals(
|
||||||
|
deriveMintAuthorityKey(TOKEN_BRIDGE_ADDRESS)
|
||||||
|
)
|
||||||
|
).is.true;
|
||||||
|
expect(mintInfo.supply).to.equal(0n);
|
||||||
|
|
||||||
|
// check metadata
|
||||||
|
const metadata = await Metadata.fromAccountAddress(
|
||||||
|
connection,
|
||||||
|
deriveTokenMetadataKey(mint)
|
||||||
|
);
|
||||||
|
expect(metadata.data.name.toString()).not.equals(oldExpectedName);
|
||||||
|
expect(metadata.data.name.toString()).equals(
|
||||||
|
`${name} (Wormhole)`.padEnd(32, "\0")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Receive Token", async () => {
|
it("Receive Token", async () => {
|
||||||
|
@ -1653,7 +1931,7 @@ describe("Token Bridge", () => {
|
||||||
).to.equal(0);
|
).to.equal(0);
|
||||||
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
|
expect(messageData.emitterChain).to.equal(ethereumTokenBridge.chain);
|
||||||
expect(messageData.nonce).to.equal(nonce);
|
expect(messageData.nonce).to.equal(nonce);
|
||||||
expect(messageData.sequence).to.equal(3n);
|
expect(messageData.sequence).to.equal(5n);
|
||||||
expect(messageData.vaaTime).to.equal(0);
|
expect(messageData.vaaTime).to.equal(0);
|
||||||
expect(messageData.vaaVersion).to.equal(1);
|
expect(messageData.vaaVersion).to.equal(1);
|
||||||
expect(
|
expect(
|
||||||
|
@ -1900,7 +2178,7 @@ describe("Token Bridge", () => {
|
||||||
// nft bridge on Ethereum
|
// nft bridge on Ethereum
|
||||||
const ethereumTokenBridge = new MockEthereumTokenBridge(
|
const ethereumTokenBridge = new MockEthereumTokenBridge(
|
||||||
ETHEREUM_TOKEN_BRIDGE_ADDRESS,
|
ETHEREUM_TOKEN_BRIDGE_ADDRESS,
|
||||||
3 // startSequence
|
10 // startSequence
|
||||||
);
|
);
|
||||||
|
|
||||||
describe("getOriginalAssetSolana", () => {
|
describe("getOriginalAssetSolana", () => {
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import * as web3 from "@solana/web3.js";
|
import * as web3 from "@solana/web3.js";
|
||||||
|
import {
|
||||||
|
Metadata,
|
||||||
|
PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID,
|
||||||
|
createCreateMetadataAccountV3Instruction,
|
||||||
|
} from "@metaplex-foundation/mpl-token-metadata";
|
||||||
import {
|
import {
|
||||||
ASSOCIATED_TOKEN_PROGRAM_ID,
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
createMint,
|
createMint,
|
||||||
|
@ -19,9 +24,8 @@ import {
|
||||||
import { postVaa } from "../../../sdk/js/src/solana/sendAndConfirmPostVaa";
|
import { postVaa } from "../../../sdk/js/src/solana/sendAndConfirmPostVaa";
|
||||||
import {
|
import {
|
||||||
BpfLoaderUpgradeable,
|
BpfLoaderUpgradeable,
|
||||||
getMetadata,
|
|
||||||
NodeWallet,
|
NodeWallet,
|
||||||
SplTokenMetadataProgram,
|
deriveTokenMetadataKey,
|
||||||
} from "../../../sdk/js/src/solana";
|
} from "../../../sdk/js/src/solana";
|
||||||
import {
|
import {
|
||||||
deriveWormholeEmitterKey,
|
deriveWormholeEmitterKey,
|
||||||
|
@ -115,26 +119,37 @@ describe("NFT Bridge", () => {
|
||||||
uri: "https://spl.solana.com/token#example-create-a-non-fungible-token",
|
uri: "https://spl.solana.com/token#example-create-a-non-fungible-token",
|
||||||
};
|
};
|
||||||
|
|
||||||
const mint = localVariables.mint;
|
const mint: web3.PublicKey = localVariables.mint;
|
||||||
const name = localVariables.nftMeta.name;
|
const name: string = localVariables.nftMeta.name;
|
||||||
const symbol = localVariables.nftMeta.symbol;
|
const symbol: string = localVariables.nftMeta.symbol;
|
||||||
const updateAuthorityIsSigner = false;
|
const uri: string = localVariables.nftMeta.uri;
|
||||||
const uri = localVariables.nftMeta.uri;
|
|
||||||
const creators = null;
|
const accounts = {
|
||||||
const sellerFeeBasisPoints = 0;
|
metadata: deriveTokenMetadataKey(mint),
|
||||||
const isMutable = false;
|
|
||||||
const createMetadataIx = SplTokenMetadataProgram.createMetadataAccounts(
|
|
||||||
wallet.key(),
|
|
||||||
mint,
|
mint,
|
||||||
wallet.key(),
|
mintAuthority: wallet.key(),
|
||||||
name,
|
payer: wallet.key(),
|
||||||
symbol,
|
updateAuthority: wallet.key(),
|
||||||
wallet.key(),
|
};
|
||||||
updateAuthorityIsSigner,
|
const args = {
|
||||||
uri,
|
createMetadataAccountArgsV3: {
|
||||||
creators,
|
data: {
|
||||||
sellerFeeBasisPoints,
|
name,
|
||||||
isMutable
|
symbol,
|
||||||
|
uri,
|
||||||
|
sellerFeeBasisPoints: 0,
|
||||||
|
creators: null,
|
||||||
|
collection: null,
|
||||||
|
uses: null,
|
||||||
|
},
|
||||||
|
isMutable: false,
|
||||||
|
collectionDetails: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const createMetadataIx = createCreateMetadataAccountV3Instruction(
|
||||||
|
accounts,
|
||||||
|
args,
|
||||||
|
TOKEN_METADATA_PROGRAM_ID
|
||||||
);
|
);
|
||||||
|
|
||||||
const createMetadataTx = await web3.sendAndConfirmTransaction(
|
const createMetadataTx = await web3.sendAndConfirmTransaction(
|
||||||
|
@ -323,9 +338,8 @@ describe("NFT Bridge", () => {
|
||||||
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
||||||
.true;
|
.true;
|
||||||
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
||||||
expect(
|
expect(accounts.splMetadataProgram.equals(TOKEN_METADATA_PROGRAM_ID)).is
|
||||||
accounts.splMetadataProgram.equals(SplTokenMetadataProgram.programId)
|
.true;
|
||||||
).is.true;
|
|
||||||
expect(
|
expect(
|
||||||
accounts.associatedTokenProgram.equals(ASSOCIATED_TOKEN_PROGRAM_ID)
|
accounts.associatedTokenProgram.equals(ASSOCIATED_TOKEN_PROGRAM_ID)
|
||||||
).is.true;
|
).is.true;
|
||||||
|
@ -401,9 +415,8 @@ describe("NFT Bridge", () => {
|
||||||
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
||||||
.true;
|
.true;
|
||||||
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
||||||
expect(
|
expect(accounts.splMetadataProgram.equals(TOKEN_METADATA_PROGRAM_ID)).is
|
||||||
accounts.splMetadataProgram.equals(SplTokenMetadataProgram.programId)
|
.true;
|
||||||
).is.true;
|
|
||||||
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -467,9 +480,8 @@ describe("NFT Bridge", () => {
|
||||||
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
||||||
.true;
|
.true;
|
||||||
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
||||||
expect(
|
expect(accounts.splMetadataProgram.equals(TOKEN_METADATA_PROGRAM_ID)).is
|
||||||
accounts.splMetadataProgram.equals(SplTokenMetadataProgram.programId)
|
.true;
|
||||||
).is.true;
|
|
||||||
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -524,9 +536,8 @@ describe("NFT Bridge", () => {
|
||||||
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
expect(accounts.systemProgram.equals(web3.SystemProgram.programId)).to.be
|
||||||
.true;
|
.true;
|
||||||
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
expect(accounts.tokenProgram.equals(TOKEN_PROGRAM_ID)).is.true;
|
||||||
expect(
|
expect(accounts.splMetadataProgram.equals(TOKEN_METADATA_PROGRAM_ID)).is
|
||||||
accounts.splMetadataProgram.equals(SplTokenMetadataProgram.programId)
|
.true;
|
||||||
).is.true;
|
|
||||||
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
expect(accounts.wormholeProgram.equals(CORE_BRIDGE_ADDRESS)).is.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -792,8 +803,9 @@ describe("NFT Bridge", () => {
|
||||||
custodyAccount
|
custodyAccount
|
||||||
).then((account) => account.amount);
|
).then((account) => account.amount);
|
||||||
|
|
||||||
const metadata = await getMetadata(connection, mint).then(
|
const metadata = await Metadata.fromAccountAddress(
|
||||||
(info) => info.data
|
connection,
|
||||||
|
deriveTokenMetadataKey(mint)
|
||||||
);
|
);
|
||||||
|
|
||||||
const tokenChain = 1;
|
const tokenChain = 1;
|
||||||
|
@ -803,10 +815,10 @@ describe("NFT Bridge", () => {
|
||||||
const message = ethereumNftBridge.publishTransferNft(
|
const message = ethereumNftBridge.publishTransferNft(
|
||||||
NFT_TRANSFER_NATIVE_TOKEN_ADDRESS.toString("hex"),
|
NFT_TRANSFER_NATIVE_TOKEN_ADDRESS.toString("hex"),
|
||||||
tokenChain,
|
tokenChain,
|
||||||
metadata.name,
|
metadata.data.name,
|
||||||
metadata.symbol,
|
metadata.data.symbol,
|
||||||
tokenId,
|
tokenId,
|
||||||
metadata.uri,
|
metadata.data.uri,
|
||||||
recipientChain,
|
recipientChain,
|
||||||
mintAta.toBuffer().toString("hex"),
|
mintAta.toBuffer().toString("hex"),
|
||||||
nonce
|
nonce
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"pubkey": "A2mcN1tknMBAgMnCUF6ZT8tEJj1QE1AuCsquvXHgpWVJ",
|
||||||
|
"account": {
|
||||||
|
"lamports": 5616720,
|
||||||
|
"data": [
|
||||||
|
"BP0KziEuVYQFCsdn5PTCokaUsJwqy3LxSbhyCV219Z2d8nzo8j6OCMEC0yd3q42xDE00sIuZJtJx185IZhkbzwkgAAAARGVhZCBCZWVmIChXb3JtaG9sZSkAAAAAAAAAAAAAAAAKAAAAQkVFRgAAAAAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"base64"
|
||||||
|
],
|
||||||
|
"owner": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
|
||||||
|
"executable": false,
|
||||||
|
"rentEpoch": 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"pubkey": "HKa8mGMJJm2qnja4xmMaoeGqF4LBpGCyXVZuaiGYFaYY",
|
||||||
|
"account": {
|
||||||
|
"lamports": 1461600,
|
||||||
|
"data": [
|
||||||
|
"AQAAAP0KziEuVYQFCsdn5PTCokaUsJwqy3LxSbhyCV219Z2dAAAAAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"base64"
|
||||||
|
],
|
||||||
|
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
|
||||||
|
"executable": false,
|
||||||
|
"rentEpoch": 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,3 +50,10 @@ export const ETHEREUM_TOKEN_BRIDGE_ADDRESS =
|
||||||
export const WETH_ADDRESS = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
|
export const WETH_ADDRESS = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
|
||||||
export const ETHEREUM_NFT_BRIDGE_ADDRESS =
|
export const ETHEREUM_NFT_BRIDGE_ADDRESS =
|
||||||
"0x26b4afb60d6c903165150c6f0aa14f8016be4aec";
|
"0x26b4afb60d6c903165150c6f0aa14f8016be4aec";
|
||||||
|
|
||||||
|
// preloaded
|
||||||
|
export const DEADBEEF_ADDRESS = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
|
||||||
|
export const DEADBEEF_MINT_ADDRESS =
|
||||||
|
"HKa8mGMJJm2qnja4xmMaoeGqF4LBpGCyXVZuaiGYFaYY";
|
||||||
|
export const DEADBEEF_METADATA_ADDRESS =
|
||||||
|
"A2mcN1tknMBAgMnCUF6ZT8tEJj1QE1AuCsquvXHgpWVJ";
|
||||||
|
|
|
@ -55,11 +55,60 @@
|
||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@metaplex-foundation/beet-solana@^0.4.0":
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.4.0.tgz#52891e78674aaa54e0031f1bca5bfbc40de12e8d"
|
||||||
|
integrity sha512-B1L94N3ZGMo53b0uOSoznbuM5GBNJ8LwSeznxBxJ+OThvfHQ4B5oMUqb+0zdLRfkKGS7Q6tpHK9P+QK0j3w2cQ==
|
||||||
|
dependencies:
|
||||||
|
"@metaplex-foundation/beet" ">=0.1.0"
|
||||||
|
"@solana/web3.js" "^1.56.2"
|
||||||
|
bs58 "^5.0.0"
|
||||||
|
debug "^4.3.4"
|
||||||
|
|
||||||
|
"@metaplex-foundation/beet@>=0.1.0", "@metaplex-foundation/beet@^0.7.1":
|
||||||
|
version "0.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.7.1.tgz#0975314211643f87b5f6f3e584fa31abcf4c612c"
|
||||||
|
integrity sha512-hNCEnS2WyCiYyko82rwuISsBY3KYpe828ubsd2ckeqZr7tl0WVLivGkoyA/qdiaaHEBGdGl71OpfWa2rqL3DiA==
|
||||||
|
dependencies:
|
||||||
|
ansicolors "^0.3.2"
|
||||||
|
bn.js "^5.2.0"
|
||||||
|
debug "^4.3.3"
|
||||||
|
|
||||||
|
"@metaplex-foundation/cusper@^0.0.2":
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz#dc2032a452d6c269e25f016aa4dd63600e2af975"
|
||||||
|
integrity sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA==
|
||||||
|
|
||||||
|
"@metaplex-foundation/mpl-token-metadata@^2.11.1":
|
||||||
|
version "2.11.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-2.11.1.tgz#b7755c5cc7bae5e98e285dd675fa65b62e9bd881"
|
||||||
|
integrity sha512-FNJhDAFmpXD5K9lstJYXROjUjHQmCHFpzVs4asUpVvtkF645+PGyDtqoUENfVEwoUPY8ZT6Exs7d0exRgYqxUA==
|
||||||
|
dependencies:
|
||||||
|
"@metaplex-foundation/beet" "^0.7.1"
|
||||||
|
"@metaplex-foundation/beet-solana" "^0.4.0"
|
||||||
|
"@metaplex-foundation/cusper" "^0.0.2"
|
||||||
|
"@solana/spl-token" "^0.3.6"
|
||||||
|
"@solana/web3.js" "^1.66.2"
|
||||||
|
bn.js "^5.2.0"
|
||||||
|
debug "^4.3.4"
|
||||||
|
|
||||||
|
"@noble/curves@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932"
|
||||||
|
integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==
|
||||||
|
dependencies:
|
||||||
|
"@noble/hashes" "1.3.0"
|
||||||
|
|
||||||
"@noble/ed25519@^1.7.0":
|
"@noble/ed25519@^1.7.0":
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.0.tgz#583ac38340a479314b9e348d4572101ed9492f9d"
|
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.0.tgz#583ac38340a479314b9e348d4572101ed9492f9d"
|
||||||
integrity sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg==
|
integrity sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg==
|
||||||
|
|
||||||
|
"@noble/hashes@1.3.0", "@noble/hashes@^1.3.0":
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1"
|
||||||
|
integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==
|
||||||
|
|
||||||
"@noble/hashes@^1.1.2":
|
"@noble/hashes@^1.1.2":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
|
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
|
||||||
|
@ -125,6 +174,15 @@
|
||||||
"@solana/buffer-layout-utils" "^0.2.0"
|
"@solana/buffer-layout-utils" "^0.2.0"
|
||||||
"@solana/web3.js" "^1.41.0"
|
"@solana/web3.js" "^1.41.0"
|
||||||
|
|
||||||
|
"@solana/spl-token@^0.3.6":
|
||||||
|
version "0.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.7.tgz#6f027f9ad8e841f792c32e50920d9d2e714fc8da"
|
||||||
|
integrity sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg==
|
||||||
|
dependencies:
|
||||||
|
"@solana/buffer-layout" "^4.0.0"
|
||||||
|
"@solana/buffer-layout-utils" "^0.2.0"
|
||||||
|
buffer "^6.0.3"
|
||||||
|
|
||||||
"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.41.0", "@solana/web3.js@^1.53.0":
|
"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.41.0", "@solana/web3.js@^1.53.0":
|
||||||
version "1.53.0"
|
version "1.53.0"
|
||||||
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.53.0.tgz"
|
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.53.0.tgz"
|
||||||
|
@ -170,6 +228,27 @@
|
||||||
rpc-websockets "^7.5.0"
|
rpc-websockets "^7.5.0"
|
||||||
superstruct "^0.14.2"
|
superstruct "^0.14.2"
|
||||||
|
|
||||||
|
"@solana/web3.js@^1.56.2", "@solana/web3.js@^1.66.2":
|
||||||
|
version "1.76.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.76.0.tgz#0f888e25d727d0dadf3dd8a01967347555200b2b"
|
||||||
|
integrity sha512-aJtF/nTs+9St+KtTK/wgVJ+SinfjYzn+3w1ygYIPw8ST6LH+qHBn8XkodgDTwlv/xzNkaVz1kkUDOZ8BPXyZWA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
"@noble/curves" "^1.0.0"
|
||||||
|
"@noble/hashes" "^1.3.0"
|
||||||
|
"@solana/buffer-layout" "^4.0.0"
|
||||||
|
agentkeepalive "^4.2.1"
|
||||||
|
bigint-buffer "^1.1.5"
|
||||||
|
bn.js "^5.0.0"
|
||||||
|
borsh "^0.7.0"
|
||||||
|
bs58 "^4.0.1"
|
||||||
|
buffer "6.0.3"
|
||||||
|
fast-stable-stringify "^1.0.0"
|
||||||
|
jayson "^3.4.4"
|
||||||
|
node-fetch "^2.6.7"
|
||||||
|
rpc-websockets "^7.5.1"
|
||||||
|
superstruct "^0.14.2"
|
||||||
|
|
||||||
"@tsconfig/node10@^1.0.7":
|
"@tsconfig/node10@^1.0.7":
|
||||||
version "1.0.9"
|
version "1.0.9"
|
||||||
resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz"
|
resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz"
|
||||||
|
@ -283,6 +362,15 @@ acorn@^8.4.1:
|
||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz"
|
||||||
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
|
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
|
||||||
|
|
||||||
|
agentkeepalive@^4.2.1:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255"
|
||||||
|
integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.1.0"
|
||||||
|
depd "^2.0.0"
|
||||||
|
humanize-ms "^1.2.1"
|
||||||
|
|
||||||
ansi-colors@4.1.1:
|
ansi-colors@4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz"
|
resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz"
|
||||||
|
@ -300,6 +388,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert "^2.0.1"
|
color-convert "^2.0.1"
|
||||||
|
|
||||||
|
ansicolors@^0.3.2:
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
|
||||||
|
integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==
|
||||||
|
|
||||||
anymatch@~3.1.2:
|
anymatch@~3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz"
|
resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz"
|
||||||
|
@ -340,6 +433,11 @@ base-x@^3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
base-x@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a"
|
||||||
|
integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==
|
||||||
|
|
||||||
base64-js@^1.3.1, base64-js@^1.5.1:
|
base64-js@^1.3.1, base64-js@^1.5.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
|
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
|
||||||
|
@ -420,6 +518,13 @@ bs58@^4.0.0, bs58@^4.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
base-x "^3.0.2"
|
base-x "^3.0.2"
|
||||||
|
|
||||||
|
bs58@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279"
|
||||||
|
integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==
|
||||||
|
dependencies:
|
||||||
|
base-x "^4.0.0"
|
||||||
|
|
||||||
buffer-from@^1.0.0, buffer-from@^1.1.0:
|
buffer-from@^1.0.0, buffer-from@^1.1.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
|
||||||
|
@ -438,6 +543,14 @@ buffer@6.0.1:
|
||||||
base64-js "^1.3.1"
|
base64-js "^1.3.1"
|
||||||
ieee754 "^1.2.1"
|
ieee754 "^1.2.1"
|
||||||
|
|
||||||
|
buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3:
|
||||||
|
version "6.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
|
||||||
|
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.2.1"
|
||||||
|
|
||||||
buffer@^5.4.3:
|
buffer@^5.4.3:
|
||||||
version "5.7.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz"
|
resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz"
|
||||||
|
@ -446,14 +559,6 @@ buffer@^5.4.3:
|
||||||
base64-js "^1.3.1"
|
base64-js "^1.3.1"
|
||||||
ieee754 "^1.1.13"
|
ieee754 "^1.1.13"
|
||||||
|
|
||||||
buffer@^6.0.3, buffer@~6.0.3:
|
|
||||||
version "6.0.3"
|
|
||||||
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
|
|
||||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
|
||||||
dependencies:
|
|
||||||
base64-js "^1.3.1"
|
|
||||||
ieee754 "^1.2.1"
|
|
||||||
|
|
||||||
bufferutil@^4.0.1:
|
bufferutil@^4.0.1:
|
||||||
version "4.0.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz"
|
resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz"
|
||||||
|
@ -577,6 +682,13 @@ debug@4.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
debug@^4.1.0, debug@^4.3.3, debug@^4.3.4:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
decamelize@^4.0.0:
|
decamelize@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
|
resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
|
||||||
|
@ -594,6 +706,11 @@ delay@^5.0.0:
|
||||||
resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz"
|
||||||
integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==
|
integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==
|
||||||
|
|
||||||
|
depd@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
|
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||||
|
|
||||||
diff@5.0.0:
|
diff@5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz"
|
||||||
|
@ -768,6 +885,13 @@ hmac-drbg@^1.0.1:
|
||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
minimalistic-crypto-utils "^1.0.1"
|
||||||
|
|
||||||
|
humanize-ms@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
|
||||||
|
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
|
||||||
|
dependencies:
|
||||||
|
ms "^2.0.0"
|
||||||
|
|
||||||
ieee754@^1.1.13, ieee754@^1.2.1:
|
ieee754@^1.1.13, ieee754@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
|
||||||
|
@ -1018,7 +1142,7 @@ ms@2.1.2:
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3, ms@^2.0.0:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
@ -1048,6 +1172,13 @@ node-fetch@2, node-fetch@2.6.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
|
node-fetch@^2.6.7:
|
||||||
|
version "2.6.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
|
||||||
|
integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
|
node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz"
|
resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz"
|
||||||
|
@ -1167,6 +1298,19 @@ rpc-websockets@^7.5.0:
|
||||||
bufferutil "^4.0.1"
|
bufferutil "^4.0.1"
|
||||||
utf-8-validate "^5.0.2"
|
utf-8-validate "^5.0.2"
|
||||||
|
|
||||||
|
rpc-websockets@^7.5.1:
|
||||||
|
version "7.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.1.tgz#e0a05d525a97e7efc31a0617f093a13a2e10c401"
|
||||||
|
integrity sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.17.2"
|
||||||
|
eventemitter3 "^4.0.7"
|
||||||
|
uuid "^8.3.2"
|
||||||
|
ws "^8.5.0"
|
||||||
|
optionalDependencies:
|
||||||
|
bufferutil "^4.0.1"
|
||||||
|
utf-8-validate "^5.0.2"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||||
|
|
Loading…
Reference in New Issue