descro playgound & sdk
This commit is contained in:
16
sdk/package.json
Normal file
16
sdk/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@descro/sdk",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.32.1",
|
||||
"@solana/web3.js": "^1.98.4",
|
||||
"bn.js": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
198
sdk/src/escrow.ts
Normal file
198
sdk/src/escrow.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import {
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { AnchorProvider, Program, BN } from "@coral-xyz/anchor";
|
||||
import type { Idl } from "@coral-xyz/anchor";
|
||||
import descroIdl from "./idl/descro.json";
|
||||
import {
|
||||
ESCROW_PROGRAM_ID,
|
||||
REGISTRY_PROGRAM_ID,
|
||||
deriveEscrowPda,
|
||||
deriveVaultPda,
|
||||
deriveEscrowAuthorityPda,
|
||||
deriveResolverEntryPda,
|
||||
} from "./pda";
|
||||
import type { EscrowAccount, EscrowAccountWithPda, Winner } from "./types";
|
||||
|
||||
export class EscrowClient {
|
||||
readonly program: Program;
|
||||
readonly provider: AnchorProvider;
|
||||
|
||||
constructor(provider: AnchorProvider) {
|
||||
this.provider = provider;
|
||||
this.program = new Program(
|
||||
descroIdl as Idl,
|
||||
new PublicKey(ESCROW_PROGRAM_ID),
|
||||
provider
|
||||
);
|
||||
}
|
||||
|
||||
async buildCreateEscrow(params: {
|
||||
seller: PublicKey;
|
||||
buyer: PublicKey;
|
||||
amount: BN;
|
||||
disputeResolver: PublicKey | null;
|
||||
escrowId: BN;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { seller, buyer, amount, disputeResolver, escrowId } = params;
|
||||
const [escrowPda] = deriveEscrowPda(seller, escrowId);
|
||||
const [vaultPda] = deriveVaultPda(escrowPda);
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.createEscrow(amount, disputeResolver, escrowId)
|
||||
.accounts({
|
||||
seller,
|
||||
buyer,
|
||||
escrowAccount: escrowPda,
|
||||
vault: vaultPda,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async buildDeposit(params: {
|
||||
buyer: PublicKey;
|
||||
escrowPda: PublicKey;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { buyer, escrowPda } = params;
|
||||
const [vaultPda] = deriveVaultPda(escrowPda);
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.deposit()
|
||||
.accounts({
|
||||
buyer,
|
||||
escrowAccount: escrowPda,
|
||||
vault: vaultPda,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async buildComplete(params: {
|
||||
buyer: PublicKey;
|
||||
seller: PublicKey;
|
||||
escrowPda: PublicKey;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { buyer, seller, escrowPda } = params;
|
||||
const [vaultPda] = deriveVaultPda(escrowPda);
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.complete()
|
||||
.accounts({
|
||||
buyer,
|
||||
seller,
|
||||
escrowAccount: escrowPda,
|
||||
vault: vaultPda,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async buildDispute(params: {
|
||||
initiator: PublicKey;
|
||||
escrowPda: PublicKey;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { initiator, escrowPda } = params;
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.dispute()
|
||||
.accounts({
|
||||
initiator,
|
||||
escrowAccount: escrowPda,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async buildCancel(params: {
|
||||
seller: PublicKey;
|
||||
escrowPda: PublicKey;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { seller, escrowPda } = params;
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.cancel()
|
||||
.accounts({
|
||||
seller,
|
||||
escrowAccount: escrowPda,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async buildResolve(params: {
|
||||
resolver: PublicKey;
|
||||
winner: "buyer" | "seller";
|
||||
winnerPubkey: PublicKey;
|
||||
seller: PublicKey;
|
||||
escrowPda: PublicKey;
|
||||
disputeResolver: PublicKey | null;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { resolver, winner, winnerPubkey, seller, escrowPda, disputeResolver } = params;
|
||||
const [vaultPda] = deriveVaultPda(escrowPda);
|
||||
const [escrowAuthority] = deriveEscrowAuthorityPda();
|
||||
|
||||
const registryProgram = disputeResolver ? REGISTRY_PROGRAM_ID : SystemProgram.programId;
|
||||
const resolverEntryAuthority = disputeResolver ?? resolver;
|
||||
const [resolverEntry] = deriveResolverEntryPda(resolverEntryAuthority);
|
||||
|
||||
const winnerArg: Winner =
|
||||
winner === "buyer" ? { buyer: {} } : { seller: {} };
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.resolve(winnerArg)
|
||||
.accounts({
|
||||
resolver,
|
||||
winner: winnerPubkey,
|
||||
seller,
|
||||
escrowAccount: escrowPda,
|
||||
vault: vaultPda,
|
||||
resolverEntry,
|
||||
escrowAuthority,
|
||||
registryProgram,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async fetchEscrow(pda: PublicKey): Promise<EscrowAccount | null> {
|
||||
try {
|
||||
const raw = await (this.program.account as any).escrowAccount.fetch(pda);
|
||||
return raw as EscrowAccount;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchAllEscrows(): Promise<EscrowAccountWithPda[]> {
|
||||
const accounts = await (this.program.account as any).escrowAccount.all();
|
||||
return accounts.map((a: { publicKey: PublicKey; account: EscrowAccount }) => ({
|
||||
pda: a.publicKey,
|
||||
account: a.account as EscrowAccount,
|
||||
}));
|
||||
}
|
||||
|
||||
async fetchEscrowsForWallet(wallet: PublicKey): Promise<EscrowAccountWithPda[]> {
|
||||
const asSeller = await (this.program.account as any).escrowAccount.all([
|
||||
{ memcmp: { offset: 8, bytes: wallet.toBase58() } },
|
||||
]);
|
||||
|
||||
const asBuyer = await (this.program.account as any).escrowAccount.all([
|
||||
{ memcmp: { offset: 8 + 32, bytes: wallet.toBase58() } },
|
||||
]);
|
||||
|
||||
const seen = new Set<string>();
|
||||
const results: EscrowAccountWithPda[] = [];
|
||||
|
||||
for (const a of [...asSeller, ...asBuyer]) {
|
||||
const key = a.publicKey.toBase58();
|
||||
if (!seen.has(key)) {
|
||||
seen.add(key);
|
||||
results.push({ pda: a.publicKey, account: a.account as EscrowAccount });
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
674
sdk/src/idl/descro.json
Normal file
674
sdk/src/idl/descro.json
Normal file
@@ -0,0 +1,674 @@
|
||||
{
|
||||
"address": "DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3",
|
||||
"metadata": {
|
||||
"name": "descro",
|
||||
"version": "0.1.0",
|
||||
"spec": "0.1.0",
|
||||
"description": "Created with Anchor"
|
||||
},
|
||||
"instructions": [
|
||||
{
|
||||
"name": "cancel",
|
||||
"discriminator": [
|
||||
232,
|
||||
219,
|
||||
223,
|
||||
41,
|
||||
219,
|
||||
236,
|
||||
220,
|
||||
190
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "seller",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "escrow_account",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.seller",
|
||||
"account": "EscrowAccount"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.escrow_id",
|
||||
"account": "EscrowAccount"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "complete",
|
||||
"discriminator": [
|
||||
0,
|
||||
77,
|
||||
224,
|
||||
147,
|
||||
136,
|
||||
25,
|
||||
88,
|
||||
76
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "buyer",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "seller",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "escrow_account",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.seller",
|
||||
"account": "EscrowAccount"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.escrow_id",
|
||||
"account": "EscrowAccount"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vault",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
118,
|
||||
97,
|
||||
117,
|
||||
108,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "create_escrow",
|
||||
"discriminator": [
|
||||
253,
|
||||
215,
|
||||
165,
|
||||
116,
|
||||
36,
|
||||
108,
|
||||
68,
|
||||
80
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "seller",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "buyer"
|
||||
},
|
||||
{
|
||||
"name": "escrow_account",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "seller"
|
||||
},
|
||||
{
|
||||
"kind": "arg",
|
||||
"path": "escrow_id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vault",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
118,
|
||||
97,
|
||||
117,
|
||||
108,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "dispute_resolver",
|
||||
"type": {
|
||||
"option": "pubkey"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "escrow_id",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"discriminator": [
|
||||
242,
|
||||
35,
|
||||
198,
|
||||
137,
|
||||
82,
|
||||
225,
|
||||
242,
|
||||
182
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "buyer",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "escrow_account",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.seller",
|
||||
"account": "EscrowAccount"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.escrow_id",
|
||||
"account": "EscrowAccount"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vault",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
118,
|
||||
97,
|
||||
117,
|
||||
108,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "dispute",
|
||||
"discriminator": [
|
||||
216,
|
||||
92,
|
||||
128,
|
||||
146,
|
||||
202,
|
||||
85,
|
||||
135,
|
||||
73
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "initiator",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "escrow_account",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.seller",
|
||||
"account": "EscrowAccount"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.escrow_id",
|
||||
"account": "EscrowAccount"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "resolve",
|
||||
"discriminator": [
|
||||
246,
|
||||
150,
|
||||
236,
|
||||
206,
|
||||
108,
|
||||
63,
|
||||
58,
|
||||
10
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "resolver",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "winner",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "seller",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "escrow_account",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.seller",
|
||||
"account": "EscrowAccount"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.escrow_id",
|
||||
"account": "EscrowAccount"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vault",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
118,
|
||||
97,
|
||||
117,
|
||||
108,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resolver_entry",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
114,
|
||||
101,
|
||||
115,
|
||||
111,
|
||||
108,
|
||||
118,
|
||||
101,
|
||||
114
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "escrow_account.dispute_resolver",
|
||||
"account": "EscrowAccount"
|
||||
}
|
||||
],
|
||||
"program": {
|
||||
"kind": "const",
|
||||
"value": [
|
||||
4,
|
||||
21,
|
||||
77,
|
||||
207,
|
||||
27,
|
||||
130,
|
||||
71,
|
||||
134,
|
||||
111,
|
||||
76,
|
||||
186,
|
||||
244,
|
||||
136,
|
||||
42,
|
||||
168,
|
||||
238,
|
||||
151,
|
||||
132,
|
||||
148,
|
||||
41,
|
||||
241,
|
||||
127,
|
||||
56,
|
||||
240,
|
||||
140,
|
||||
66,
|
||||
190,
|
||||
194,
|
||||
253,
|
||||
243,
|
||||
165,
|
||||
91
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "escrow_authority",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119,
|
||||
95,
|
||||
97,
|
||||
117,
|
||||
116,
|
||||
104,
|
||||
111,
|
||||
114,
|
||||
105,
|
||||
116,
|
||||
121
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "registry_program"
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "winner",
|
||||
"type": {
|
||||
"defined": {
|
||||
"name": "Winner"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "EscrowAccount",
|
||||
"discriminator": [
|
||||
36,
|
||||
69,
|
||||
48,
|
||||
18,
|
||||
128,
|
||||
225,
|
||||
125,
|
||||
135
|
||||
]
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{
|
||||
"code": 6000,
|
||||
"name": "InvalidState",
|
||||
"msg": "Invalid state for this instruction"
|
||||
},
|
||||
{
|
||||
"code": 6001,
|
||||
"name": "Unauthorized",
|
||||
"msg": "Signer is not authorized"
|
||||
},
|
||||
{
|
||||
"code": 6002,
|
||||
"name": "NoResolverConfigured",
|
||||
"msg": "No resolver configured for this escrow"
|
||||
},
|
||||
{
|
||||
"code": 6003,
|
||||
"name": "UnauthorizedResolver",
|
||||
"msg": "Signer is not the configured resolver"
|
||||
},
|
||||
{
|
||||
"code": 6004,
|
||||
"name": "Expired",
|
||||
"msg": "Escrow has expired"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"name": "EscrowAccount",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "seller",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "buyer",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "dispute_resolver",
|
||||
"type": {
|
||||
"option": "pubkey"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "state",
|
||||
"type": {
|
||||
"defined": {
|
||||
"name": "EscrowState"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bump",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "vault_bump",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "escrow_id",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "EscrowState",
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"variants": [
|
||||
{
|
||||
"name": "AwaitingDeposit"
|
||||
},
|
||||
{
|
||||
"name": "Active"
|
||||
},
|
||||
{
|
||||
"name": "Disputed"
|
||||
},
|
||||
{
|
||||
"name": "Complete"
|
||||
},
|
||||
{
|
||||
"name": "Cancelled"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Winner",
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"variants": [
|
||||
{
|
||||
"name": "Buyer"
|
||||
},
|
||||
{
|
||||
"name": "Seller"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
385
sdk/src/idl/descro_ext_resolvers.json
Normal file
385
sdk/src/idl/descro_ext_resolvers.json
Normal file
@@ -0,0 +1,385 @@
|
||||
{
|
||||
"address": "GwUPAKs3HHzCpj8uhet4NAnxk9GWNwfrYbpihu5DyFp",
|
||||
"metadata": {
|
||||
"name": "descro_ext_resolvers",
|
||||
"version": "0.1.0",
|
||||
"spec": "0.1.0",
|
||||
"description": "Resolver Registry for the descro escrow protocol"
|
||||
},
|
||||
"instructions": [
|
||||
{
|
||||
"name": "register_resolver",
|
||||
"discriminator": [
|
||||
76,
|
||||
101,
|
||||
253,
|
||||
229,
|
||||
153,
|
||||
242,
|
||||
212,
|
||||
230
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "authority",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "resolver_entry",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
114,
|
||||
101,
|
||||
115,
|
||||
111,
|
||||
108,
|
||||
118,
|
||||
101,
|
||||
114
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "authority"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "resolver_type",
|
||||
"type": {
|
||||
"defined": {
|
||||
"name": "ResolverType"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "fee_bps",
|
||||
"type": "u16"
|
||||
},
|
||||
{
|
||||
"name": "fee_recipient",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "metadata_uri",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "update_resolver",
|
||||
"discriminator": [
|
||||
108,
|
||||
227,
|
||||
28,
|
||||
163,
|
||||
123,
|
||||
230,
|
||||
190,
|
||||
84
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "authority",
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "resolver_entry",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
114,
|
||||
101,
|
||||
115,
|
||||
111,
|
||||
108,
|
||||
118,
|
||||
101,
|
||||
114
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "authority"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "fee_bps",
|
||||
"type": "u16"
|
||||
},
|
||||
{
|
||||
"name": "metadata_uri",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "update_stats",
|
||||
"discriminator": [
|
||||
145,
|
||||
138,
|
||||
9,
|
||||
150,
|
||||
178,
|
||||
31,
|
||||
158,
|
||||
244
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "escrow_authority",
|
||||
"docs": [
|
||||
"Must be the Escrow Program's authority PDA — only it can sign this."
|
||||
],
|
||||
"signer": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
101,
|
||||
115,
|
||||
99,
|
||||
114,
|
||||
111,
|
||||
119,
|
||||
95,
|
||||
97,
|
||||
117,
|
||||
116,
|
||||
104,
|
||||
111,
|
||||
114,
|
||||
105,
|
||||
116,
|
||||
121
|
||||
]
|
||||
}
|
||||
],
|
||||
"program": {
|
||||
"kind": "const",
|
||||
"value": [
|
||||
189,
|
||||
46,
|
||||
196,
|
||||
9,
|
||||
38,
|
||||
68,
|
||||
13,
|
||||
236,
|
||||
154,
|
||||
101,
|
||||
74,
|
||||
161,
|
||||
29,
|
||||
155,
|
||||
110,
|
||||
167,
|
||||
230,
|
||||
204,
|
||||
79,
|
||||
86,
|
||||
74,
|
||||
46,
|
||||
229,
|
||||
168,
|
||||
214,
|
||||
228,
|
||||
65,
|
||||
126,
|
||||
160,
|
||||
53,
|
||||
53,
|
||||
204
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resolver_entry",
|
||||
"writable": true
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "ruling",
|
||||
"type": {
|
||||
"defined": {
|
||||
"name": "Ruling"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "ResolverEntry",
|
||||
"discriminator": [
|
||||
0,
|
||||
60,
|
||||
55,
|
||||
58,
|
||||
157,
|
||||
135,
|
||||
51,
|
||||
191
|
||||
]
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{
|
||||
"code": 6000,
|
||||
"name": "Unauthorized",
|
||||
"msg": "Signer is not the registered authority"
|
||||
},
|
||||
{
|
||||
"code": 6001,
|
||||
"name": "UnauthorizedCaller",
|
||||
"msg": "Caller is not the authorized escrow program"
|
||||
},
|
||||
{
|
||||
"code": 6002,
|
||||
"name": "InvalidFeeBps",
|
||||
"msg": "Fee basis points must be <= 10000"
|
||||
},
|
||||
{
|
||||
"code": 6003,
|
||||
"name": "EmptyName",
|
||||
"msg": "Name must not be empty"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"name": "ResolverEntry",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "authority",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "resolver_type",
|
||||
"type": {
|
||||
"defined": {
|
||||
"name": "ResolverType"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "fee_bps",
|
||||
"type": "u16"
|
||||
},
|
||||
{
|
||||
"name": "fee_recipient",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "metadata_uri",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "total_resolved",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "ruled_for_buyer",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "ruled_for_seller",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "registered_at",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ResolverType",
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"variants": [
|
||||
{
|
||||
"name": "CentralAuthority"
|
||||
},
|
||||
{
|
||||
"name": "JuryDAO"
|
||||
},
|
||||
{
|
||||
"name": "MAD"
|
||||
},
|
||||
{
|
||||
"name": "Algorithmic"
|
||||
},
|
||||
{
|
||||
"name": "Multisig"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Ruling",
|
||||
"docs": [
|
||||
"Passed to update_stats; mirrors the Escrow program's Winner enum."
|
||||
],
|
||||
"type": {
|
||||
"kind": "enum",
|
||||
"variants": [
|
||||
{
|
||||
"name": "Buyer"
|
||||
},
|
||||
{
|
||||
"name": "Seller"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
19
sdk/src/index.ts
Normal file
19
sdk/src/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export * from "./types";
|
||||
export * from "./pda";
|
||||
export * from "./escrow";
|
||||
export * from "./registry";
|
||||
export * from "./listener";
|
||||
|
||||
import { AnchorProvider } from "@coral-xyz/anchor";
|
||||
import { EscrowClient } from "./escrow";
|
||||
import { RegistryClient } from "./registry";
|
||||
|
||||
export class DescroSdk {
|
||||
readonly escrow: EscrowClient;
|
||||
readonly registry: RegistryClient;
|
||||
|
||||
constructor(provider: AnchorProvider) {
|
||||
this.escrow = new EscrowClient(provider);
|
||||
this.registry = new RegistryClient(provider);
|
||||
}
|
||||
}
|
||||
53
sdk/src/listener.ts
Normal file
53
sdk/src/listener.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Connection, PublicKey, AccountInfo } from "@solana/web3.js";
|
||||
import { AnchorProvider, Program } from "@coral-xyz/anchor";
|
||||
import type { Idl } from "@coral-xyz/anchor";
|
||||
import descroIdl from "./idl/descro.json";
|
||||
import { ESCROW_PROGRAM_ID } from "./pda";
|
||||
import type { EscrowAccount } from "./types";
|
||||
|
||||
function decodeEscrowAccount(
|
||||
program: Program,
|
||||
info: AccountInfo<Buffer>
|
||||
): EscrowAccount | null {
|
||||
try {
|
||||
return (program.coder.accounts as any).decode("EscrowAccount", info.data) as EscrowAccount;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribeEscrow(
|
||||
connection: Connection,
|
||||
pda: PublicKey,
|
||||
cb: (account: EscrowAccount | null) => void
|
||||
): () => void {
|
||||
const provider = new AnchorProvider(connection, {} as never, {});
|
||||
const program = new Program(descroIdl as Idl, new PublicKey(ESCROW_PROGRAM_ID), provider);
|
||||
|
||||
const subId = connection.onAccountChange(pda, (info) => {
|
||||
cb(decodeEscrowAccount(program, info as AccountInfo<Buffer>));
|
||||
});
|
||||
|
||||
return () => {
|
||||
connection.removeAccountChangeListener(subId).catch(() => undefined);
|
||||
};
|
||||
}
|
||||
|
||||
export function subscribeAll(
|
||||
connection: Connection,
|
||||
programId: PublicKey = ESCROW_PROGRAM_ID,
|
||||
cb: (pda: PublicKey, account: EscrowAccount | null) => void
|
||||
): () => void {
|
||||
const provider = new AnchorProvider(connection, {} as never, {});
|
||||
const program = new Program(descroIdl as Idl, new PublicKey(programId), provider);
|
||||
|
||||
const subId = connection.onProgramAccountChange(programId, (keyedAccountInfo) => {
|
||||
const pda = keyedAccountInfo.accountId;
|
||||
const info = keyedAccountInfo.accountInfo;
|
||||
cb(pda, decodeEscrowAccount(program, info as AccountInfo<Buffer>));
|
||||
});
|
||||
|
||||
return () => {
|
||||
connection.removeProgramAccountChangeListener(subId).catch(() => undefined);
|
||||
};
|
||||
}
|
||||
52
sdk/src/pda.ts
Normal file
52
sdk/src/pda.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
|
||||
export const ESCROW_PROGRAM_ID = new PublicKey(
|
||||
"DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3"
|
||||
);
|
||||
export const REGISTRY_PROGRAM_ID = new PublicKey(
|
||||
"GwUPAKs3HHzCpj8uhet4NAnxk9GWNwfrYbpihu5DyFp"
|
||||
);
|
||||
|
||||
export function deriveEscrowPda(
|
||||
seller: PublicKey,
|
||||
escrowId: BN | bigint | number,
|
||||
programId: PublicKey = ESCROW_PROGRAM_ID
|
||||
): [PublicKey, number] {
|
||||
const id = new BN(escrowId.toString());
|
||||
const idBuf = Buffer.alloc(8);
|
||||
idBuf.writeBigUInt64LE(BigInt(id.toString()));
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("escrow"), seller.toBuffer(), idBuf],
|
||||
programId
|
||||
);
|
||||
}
|
||||
|
||||
export function deriveVaultPda(
|
||||
escrowPda: PublicKey,
|
||||
programId: PublicKey = ESCROW_PROGRAM_ID
|
||||
): [PublicKey, number] {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("vault"), escrowPda.toBuffer()],
|
||||
programId
|
||||
);
|
||||
}
|
||||
|
||||
export function deriveEscrowAuthorityPda(
|
||||
programId: PublicKey = ESCROW_PROGRAM_ID
|
||||
): [PublicKey, number] {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("escrow_authority")],
|
||||
programId
|
||||
);
|
||||
}
|
||||
|
||||
export function deriveResolverEntryPda(
|
||||
authority: PublicKey,
|
||||
registryProgramId: PublicKey = REGISTRY_PROGRAM_ID
|
||||
): [PublicKey, number] {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("resolver"), authority.toBuffer()],
|
||||
registryProgramId
|
||||
);
|
||||
}
|
||||
88
sdk/src/registry.ts
Normal file
88
sdk/src/registry.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { PublicKey, TransactionInstruction, SystemProgram } from "@solana/web3.js";
|
||||
import { AnchorProvider, Program } from "@coral-xyz/anchor";
|
||||
import type { Idl } from "@coral-xyz/anchor";
|
||||
import registryIdl from "./idl/descro_ext_resolvers.json";
|
||||
import { REGISTRY_PROGRAM_ID, deriveResolverEntryPda } from "./pda";
|
||||
import type { ResolverEntry, ResolverEntryWithPda, ResolverType } from "./types";
|
||||
|
||||
export class RegistryClient {
|
||||
readonly program: Program;
|
||||
readonly provider: AnchorProvider;
|
||||
|
||||
constructor(provider: AnchorProvider) {
|
||||
this.provider = provider;
|
||||
this.program = new Program(
|
||||
registryIdl as Idl,
|
||||
new PublicKey(REGISTRY_PROGRAM_ID),
|
||||
provider
|
||||
);
|
||||
}
|
||||
|
||||
async buildRegisterResolver(params: {
|
||||
authority: PublicKey;
|
||||
resolverType: ResolverType;
|
||||
name: string;
|
||||
description: string;
|
||||
feeBps: number;
|
||||
feeRecipient: PublicKey;
|
||||
metadataUri: string;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { authority, resolverType, name, description, feeBps, feeRecipient, metadataUri } = params;
|
||||
const [resolverEntry] = deriveResolverEntryPda(authority);
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.registerResolver(resolverType, name, description, feeBps, feeRecipient, metadataUri)
|
||||
.accounts({
|
||||
authority,
|
||||
resolverEntry,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async buildUpdateResolver(params: {
|
||||
authority: PublicKey;
|
||||
name: string;
|
||||
description: string;
|
||||
feeBps: number;
|
||||
metadataUri: string;
|
||||
}): Promise<TransactionInstruction> {
|
||||
const { authority, name, description, feeBps, metadataUri } = params;
|
||||
const [resolverEntry] = deriveResolverEntryPda(authority);
|
||||
|
||||
return await (this.program.methods as any)
|
||||
.updateResolver(name, description, feeBps, metadataUri)
|
||||
.accounts({
|
||||
authority,
|
||||
resolverEntry,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
async fetchResolver(authority: PublicKey): Promise<ResolverEntry | null> {
|
||||
try {
|
||||
const [pda] = deriveResolverEntryPda(authority);
|
||||
const raw = await (this.program.account as any).resolverEntry.fetch(pda);
|
||||
return raw as ResolverEntry;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchResolverByPda(pda: PublicKey): Promise<ResolverEntry | null> {
|
||||
try {
|
||||
const raw = await (this.program.account as any).resolverEntry.fetch(pda);
|
||||
return raw as ResolverEntry;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchAllResolvers(): Promise<ResolverEntryWithPda[]> {
|
||||
const accounts = await (this.program.account as any).resolverEntry.all();
|
||||
return accounts.map((a: { publicKey: PublicKey; account: ResolverEntry }) => ({
|
||||
pda: a.publicKey,
|
||||
account: a.account as ResolverEntry,
|
||||
}));
|
||||
}
|
||||
}
|
||||
87
sdk/src/types.ts
Normal file
87
sdk/src/types.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
|
||||
export type EscrowState =
|
||||
| { awaitingDeposit: Record<string, never> }
|
||||
| { active: Record<string, never> }
|
||||
| { disputed: Record<string, never> }
|
||||
| { complete: Record<string, never> }
|
||||
| { cancelled: Record<string, never> };
|
||||
|
||||
export type Winner = { buyer: Record<string, never> } | { seller: Record<string, never> };
|
||||
|
||||
export type ResolverType =
|
||||
| { centralAuthority: Record<string, never> }
|
||||
| { juryDAO: Record<string, never> }
|
||||
| { mad: Record<string, never> }
|
||||
| { algorithmic: Record<string, never> }
|
||||
| { multisig: Record<string, never> };
|
||||
|
||||
export interface EscrowAccount {
|
||||
seller: PublicKey;
|
||||
buyer: PublicKey;
|
||||
amount: BN;
|
||||
disputeResolver: PublicKey | null;
|
||||
state: EscrowState;
|
||||
bump: number;
|
||||
vaultBump: number;
|
||||
escrowId: BN;
|
||||
}
|
||||
|
||||
export interface ResolverEntry {
|
||||
authority: PublicKey;
|
||||
resolverType: ResolverType;
|
||||
name: string;
|
||||
description: string;
|
||||
feeBps: number;
|
||||
feeRecipient: PublicKey;
|
||||
metadataUri: string;
|
||||
totalResolved: BN;
|
||||
ruledForBuyer: BN;
|
||||
ruledForSeller: BN;
|
||||
registeredAt: BN;
|
||||
}
|
||||
|
||||
export interface EscrowAccountWithPda {
|
||||
pda: PublicKey;
|
||||
account: EscrowAccount;
|
||||
}
|
||||
|
||||
export interface ResolverEntryWithPda {
|
||||
pda: PublicKey;
|
||||
account: ResolverEntry;
|
||||
}
|
||||
|
||||
export function isAwaitingDeposit(state: EscrowState): boolean {
|
||||
return "awaitingDeposit" in state;
|
||||
}
|
||||
export function isActive(state: EscrowState): boolean {
|
||||
return "active" in state;
|
||||
}
|
||||
export function isDisputed(state: EscrowState): boolean {
|
||||
return "disputed" in state;
|
||||
}
|
||||
export function isComplete(state: EscrowState): boolean {
|
||||
return "complete" in state;
|
||||
}
|
||||
export function isCancelled(state: EscrowState): boolean {
|
||||
return "cancelled" in state;
|
||||
}
|
||||
|
||||
export function escrowStateLabel(state: EscrowState): string {
|
||||
if (isAwaitingDeposit(state)) return "AwaitingDeposit";
|
||||
if (isActive(state)) return "Active";
|
||||
if (isDisputed(state)) return "Disputed";
|
||||
if (isComplete(state)) return "Complete";
|
||||
if (isCancelled(state)) return "Cancelled";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
export function resolverTypeLabel(rt: ResolverType): string {
|
||||
if ("centralAuthority" in rt) return "CentralAuthority";
|
||||
if ("juryDAO" in rt) return "JuryDAO";
|
||||
if ("mad" in rt) return "MAD";
|
||||
if ("algorithmic" in rt) return "Algorithmic";
|
||||
if ("multisig" in rt) return "Multisig";
|
||||
return "Unknown";
|
||||
}
|
||||
16
sdk/tsconfig.json
Normal file
16
sdk/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2020"],
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user