@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
322 lines (321 loc) • 14.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const privySvmWalletProvider_1 = require("./privySvmWalletProvider");
const web3_js_1 = require("@solana/web3.js");
const solanaNetworks = __importStar(require("../network/svm"));
global.fetch = jest.fn(() => Promise.resolve({
ok: true,
json: () => Promise.resolve({}),
}));
jest.mock("../analytics", () => ({
sendAnalyticsEvent: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock("@privy-io/server-auth", () => ({
PrivyClient: jest.fn().mockImplementation(() => ({
walletApi: {
getWallet: jest.fn().mockResolvedValue({
id: "test-wallet-id",
address: "AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM",
}),
create: jest.fn().mockResolvedValue({
id: "test-wallet-id",
address: "AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM",
}),
solana: {
signTransaction: jest.fn().mockResolvedValue({
signedTransaction: "mock-signed-transaction",
}),
signAndSendTransaction: jest.fn().mockResolvedValue({
hash: "mock-hash",
}),
},
},
})),
}));
jest.mock("@solana/web3.js", () => {
const actual = jest.requireActual("@solana/web3.js");
return {
...actual,
Connection: jest.fn().mockImplementation(() => ({
getGenesisHash: jest.fn().mockResolvedValue(solanaNetworks.SOLANA_DEVNET_GENESIS_BLOCK_HASH),
getBalance: jest.fn().mockResolvedValue(1000000000),
getSignatureStatus: jest.fn().mockResolvedValue({
context: { slot: 123 },
value: { slot: 123, confirmations: 10, err: null },
}),
confirmTransaction: jest.fn().mockResolvedValue({
context: { slot: 123 },
value: { err: null },
}),
})),
PublicKey: jest.fn().mockImplementation(address => ({
toBase58: jest.fn().mockReturnValue(address),
})),
VersionedTransaction: jest.fn().mockImplementation(() => ({
signatures: [],
message: { compiledMessage: Buffer.from([]) },
})),
clusterApiUrl: jest.fn().mockImplementation(network => `https://api.${network}.solana.com`),
};
});
jest.mock("./privyShared", () => ({
createPrivyWallet: jest.fn().mockResolvedValue({
wallet: {
id: "test-wallet-id",
address: "AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM",
},
privy: {
walletApi: {
solana: {
signTransaction: jest.fn().mockResolvedValue({
signedTransaction: "mock-signed-transaction",
}),
signAndSendTransaction: jest.fn().mockResolvedValue({
hash: "mock-hash",
}),
},
},
},
}),
}));
jest.mock("../network/svm", () => {
const SOLANA_DEVNET_GENESIS_BLOCK_HASH = "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG";
const SOLANA_TESTNET_GENESIS_BLOCK_HASH = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY";
const SOLANA_MAINNET_GENESIS_BLOCK_HASH = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
return {
SOLANA_DEVNET_GENESIS_BLOCK_HASH,
SOLANA_TESTNET_GENESIS_BLOCK_HASH,
SOLANA_MAINNET_GENESIS_BLOCK_HASH,
SOLANA_CLUSTER_ID_BY_NETWORK_ID: {
devnet: "devnet",
testnet: "testnet",
"mainnet-beta": "mainnet-beta",
},
SOLANA_NETWORKS: {
[SOLANA_DEVNET_GENESIS_BLOCK_HASH]: {
protocolFamily: "solana",
chainId: "devnet",
networkId: "devnet",
},
[SOLANA_TESTNET_GENESIS_BLOCK_HASH]: {
protocolFamily: "solana",
chainId: "testnet",
networkId: "testnet",
},
[SOLANA_MAINNET_GENESIS_BLOCK_HASH]: {
protocolFamily: "solana",
chainId: "mainnet-beta",
networkId: "mainnet-beta",
},
},
};
});
describe("PrivySvmWalletProvider", () => {
const MOCK_ADDRESS = "AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM";
const MOCK_CONFIG = {
appId: "test-app-id",
appSecret: "test-app-secret",
walletType: "server",
};
const MOCK_CONFIG_WITH_WALLET_ID = {
...MOCK_CONFIG,
walletId: "test-wallet-id",
};
const MOCK_CONFIG_WITH_NETWORK_ID = {
...MOCK_CONFIG,
networkId: "devnet",
};
const MOCK_CONFIG_WITH_AUTH_KEY = {
...MOCK_CONFIG,
authorizationPrivateKey: "test-auth-key",
authorizationKeyId: "test-auth-key-id",
};
beforeEach(() => {
jest.clearAllMocks();
});
describe("configureWithWallet", () => {
it("should configure with default settings", async () => {
const provider = await privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet(MOCK_CONFIG);
expect(provider).toBeInstanceOf(privySvmWalletProvider_1.PrivySvmWalletProvider);
});
it("should configure with an existing wallet ID", async () => {
const provider = await privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet(MOCK_CONFIG_WITH_WALLET_ID);
expect(provider).toBeInstanceOf(privySvmWalletProvider_1.PrivySvmWalletProvider);
});
it("should configure with a specified network ID", async () => {
const provider = await privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet(MOCK_CONFIG_WITH_NETWORK_ID);
expect(provider).toBeInstanceOf(privySvmWalletProvider_1.PrivySvmWalletProvider);
});
it("should configure with authorization keys", async () => {
const provider = await privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet(MOCK_CONFIG_WITH_AUTH_KEY);
expect(provider).toBeInstanceOf(privySvmWalletProvider_1.PrivySvmWalletProvider);
});
it("should configure with a custom connection", async () => {
const connection = new web3_js_1.Connection("https://custom-rpc.example.com");
const provider = await privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet({
...MOCK_CONFIG,
connection,
});
expect(provider).toBeInstanceOf(privySvmWalletProvider_1.PrivySvmWalletProvider);
});
it("should handle configuration with invalid network", async () => {
const mockClusterApiUrl = jest.fn().mockImplementation(() => {
throw new Error("Invalid cluster");
});
const webThreeJs = jest.requireMock("@solana/web3.js");
const originalFn = webThreeJs.clusterApiUrl;
webThreeJs.clusterApiUrl = mockClusterApiUrl;
await expect(privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet({
...MOCK_CONFIG,
networkId: "this-network-definitely-does-not-exist",
})).rejects.toThrow();
webThreeJs.clusterApiUrl = originalFn;
});
});
describe("wallet methods", () => {
let provider;
let mockTransaction;
beforeEach(async () => {
provider = await privySvmWalletProvider_1.PrivySvmWalletProvider.configureWithWallet(MOCK_CONFIG);
mockTransaction = {
message: { compiledMessage: Buffer.from([]) },
signatures: [],
};
});
it("should get the wallet address", () => {
expect(provider.getAddress()).toBe(MOCK_ADDRESS);
});
it("should get the network information", () => {
expect(provider.getNetwork()).toEqual({
protocolFamily: "solana",
chainId: "devnet",
networkId: "devnet",
});
});
it("should get the provider name", () => {
expect(provider.getName()).toBe("privy_svm_wallet_provider");
});
it("should get the wallet balance", async () => {
const balance = await provider.getBalance();
expect(balance).toBe(BigInt(1000000000));
});
it("should sign a transaction", async () => {
const signedTx = await provider.signTransaction(mockTransaction);
expect(signedTx).toBe("mock-signed-transaction");
});
it("should sign and send a transaction", async () => {
const hash = await provider.signAndSendTransaction(mockTransaction);
expect(hash).toBe("mock-hash");
});
it("should throw an error when sending a transaction directly", async () => {
await expect(provider.sendTransaction(mockTransaction)).rejects.toThrow("Method not implemented");
});
it("should throw an error when transferring native tokens", async () => {
await expect(provider.nativeTransfer("destination-address", "1.0")).rejects.toThrow("Method not implemented");
});
it("should get the signature status", async () => {
const status = await provider.getSignatureStatus("mock-signature");
expect(status).toEqual({
context: { slot: 123 },
value: { slot: 123, confirmations: 10, err: null },
});
});
it("should wait for signature result", async () => {
const result = await provider.waitForSignatureResult("mock-signature");
expect(result).toEqual({
context: { slot: 123 },
value: { err: null },
});
});
it("should get the connection", () => {
const connection = provider.getConnection();
expect(connection).toBeDefined();
});
it("should get the public key", () => {
const publicKey = provider.getPublicKey();
expect(publicKey.toBase58()).toBe(MOCK_ADDRESS);
});
it("should export wallet data", () => {
const walletData = provider.exportWallet();
expect(walletData).toEqual({
walletId: "test-wallet-id",
authorizationPrivateKey: undefined,
chainId: "devnet",
networkId: "devnet",
});
});
it("should handle errors when signing transaction", async () => {
jest.spyOn(provider, "signTransaction").mockRejectedValueOnce(new Error("Signing failed"));
const mockTransaction = {
message: { compiledMessage: Buffer.from([]) },
signatures: [],
};
await expect(provider.signTransaction(mockTransaction)).rejects.toThrow("Signing failed");
});
it("should handle errors when signing and sending transaction", async () => {
jest
.spyOn(provider, "signAndSendTransaction")
.mockRejectedValueOnce(new Error("Failed to send transaction"));
const mockTransaction = {
message: { compiledMessage: Buffer.from([]) },
signatures: [],
};
await expect(provider.signAndSendTransaction(mockTransaction)).rejects.toThrow("Failed to send transaction");
});
it("should handle timeout during transaction signature status check", async () => {
const mockMethod = jest.fn().mockRejectedValueOnce(new Error("Request timed out"));
const connection = provider.getConnection();
connection.getSignatureStatus = mockMethod;
await expect(provider.getSignatureStatus("mock-signature")).rejects.toThrow("Request timed out");
});
it("should handle network errors during balance check", async () => {
const mockMethod = jest.fn().mockRejectedValueOnce(new Error("RPC endpoint error"));
const connection = provider.getConnection();
connection.getBalance = mockMethod;
await expect(provider.getBalance()).rejects.toThrow("RPC endpoint error");
});
it("should throw error when trying to get KeyPairSigner", async () => {
await expect(provider.getKeyPairSigner()).rejects.toThrow("getKeyPairSigner is not supported for PrivySvmWalletProvider");
});
it("should throw error when trying to convert to signer", async () => {
await expect(provider.toSigner()).rejects.toThrow("getKeyPairSigner is not supported for PrivySvmWalletProvider");
});
it("should return false for isKeyPairSigner", async () => {
const isValid = await provider.isKeyPairSigner();
expect(isValid).toBe(false);
});
});
});