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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion contracts-sol/tests/escrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
tokenBalance,
createVault,
initNonce,
jobPda,
noncePda,
vaultAuthorityPda,
payload,
expectRevert,
BN,
Expand All @@ -29,16 +31,19 @@ describe("escrow (custody)", () => {
});

it("creates a vault bound to a settlement program", async () => {
const { vault, vaultToken } = await createVault(
const { vaultId, vault, vaultToken } = await createVault(
jobProgram.programId,
mint,
feeAccount,
"escrow-create",
);
const acc = await escrowProgram.account.vault.fetch(vault);
assert.equal(acc.settlementProgram.toBase58(), jobProgram.programId.toBase58());
assert.equal(acc.owner.toBase58(), jobPda(vaultId).toBase58());
assert.equal(acc.authority.toBase58(), vaultAuthorityPda(jobProgram.programId).toBase58());
assert.equal(acc.mint.toBase58(), mint.toBase58());
assert.equal(acc.tokenAccount.toBase58(), vaultToken.toBase58());
assert.equal(acc.feeAccount.toBase58(), feeAccount.toBase58());
assert.equal(acc.deposited.toNumber(), 0);
assert.equal(acc.disbursed.toNumber(), 0);
});
Expand Down
92 changes: 87 additions & 5 deletions contracts-sol/tests/settlement_game_poker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ describe("settlement_game_poker", () => {
await fundSol(settler.publicKey);
});

async function expectGameState(
game: PublicKey,
state: "open" | "settled" | "cancelled",
) {
assert.deepEqual((await pokerProgram.account.game.fetch(game)).state, {
[state]: {},
});
}

async function newGame(label: string) {
const { vault, vaultToken } = await createVault(pokerProgram.programId, mint, feeAccount, label);
const gameId = id32(label);
Expand All @@ -49,15 +58,23 @@ describe("settlement_game_poker", () => {
.accounts({ game, creator: payer.publicKey, vault, systemProgram: SystemProgram.programId })
.signers([])
.rpc();
await expectGameState(game, "open");
return { vault, vaultToken, game, gameId };
}

async function join(game: PublicKey, vault: PublicKey, vaultToken: PublicKey, player: Keypair, nonce: number) {
async function join(
game: PublicKey,
vault: PublicKey,
vaultToken: PublicKey,
player: Keypair,
nonce: number,
amount = STAKE,
) {
await fundSol(player.publicKey);
const playerToken = await fundTokens(mint, player.publicKey, STAKE);
await initNonce(player);
await pokerProgram.methods
.join(payload(player.publicKey, STAKE, nonce))
.join(payload(player.publicKey, amount, nonce))
.accounts({
game,
playerEntry: playerPda(game, player.publicKey),
Expand All @@ -72,6 +89,13 @@ describe("settlement_game_poker", () => {
})
.signers([player])
.rpc();

const entry = await pokerProgram.account.playerEntry.fetch(playerPda(game, player.publicKey));
assert.equal(entry.game.toBase58(), game.toBase58());
assert.equal(entry.player.toBase58(), player.publicKey.toBase58());
assert.equal(entry.amount.toNumber(), amount);
assert.equal(entry.refunded, false);
return playerToken;
}

it("two players join and the settler awards the pot minus rake", async () => {
Expand All @@ -83,6 +107,7 @@ describe("settlement_game_poker", () => {

assert.equal((await pokerProgram.account.game.fetch(game)).playerCount, 2);
assert.equal(await tokenBalance(vaultToken), BigInt(2 * STAKE));
await expectGameState(game, "open");

const winnerToken = await ata(mint, p1.publicKey);
const feeBefore = await tokenBalance(feeAccount);
Expand All @@ -108,6 +133,10 @@ describe("settlement_game_poker", () => {
const fee = Math.floor((pot * FEE_BPS) / 10000); // 100
assert.equal(await tokenBalance(winnerToken), BigInt(pot - fee));
assert.equal((await tokenBalance(feeAccount)) - feeBefore, BigInt(fee));
await expectGameState(game, "settled");

const latePlayer = Keypair.generate();
await expectRevert(join(game, vault, vaultToken, latePlayer, 1), "join after settle");
});

it("cancel + claim_refund returns each player's stake", async () => {
Expand All @@ -118,9 +147,31 @@ describe("settlement_game_poker", () => {
await join(game, vault, vaultToken, p2, 1);

await pokerProgram.methods.cancel().accounts({ game, settler: settler.publicKey }).signers([settler]).rpc();
await expectGameState(game, "cancelled");

await expectRevert(
pokerProgram.methods
.settle()
.accounts({
game,
settler: settler.publicKey,
winnerEntry: playerPda(game, p1.publicKey),
vault,
vaultAuthority: vaultAuthority(),
vaultToken,
winnerToken: await ata(mint, p1.publicKey),
feeToken: feeAccount,
tokenProgram: TOKEN_PROGRAM_ID,
escrowProgram: escrowProgram.programId,
})
.signers([settler])
.rpc(),
"settle after cancel",
);

for (const p of [p1, p2]) {
const before = await tokenBalance(await ata(mint, p.publicKey));
const playerToken = await ata(mint, p.publicKey);
const before = await tokenBalance(playerToken);
await pokerProgram.methods
.claimRefund()
.accounts({
Expand All @@ -130,15 +181,37 @@ describe("settlement_game_poker", () => {
vault,
vaultAuthority: vaultAuthority(),
vaultToken,
playerToken: await ata(mint, p.publicKey),
playerToken,
feeToken: feeAccount,
tokenProgram: TOKEN_PROGRAM_ID,
escrowProgram: escrowProgram.programId,
})
.signers([p])
.rpc();
assert.equal((await tokenBalance(await ata(mint, p.publicKey))) - before, BigInt(STAKE));
assert.equal((await tokenBalance(playerToken)) - before, BigInt(STAKE));
const entry = await pokerProgram.account.playerEntry.fetch(playerPda(game, p.publicKey));
assert.equal(entry.refunded, true);
}

await expectRevert(
pokerProgram.methods
.claimRefund()
.accounts({
game,
playerEntry: playerPda(game, p1.publicKey),
player: p1.publicKey,
vault,
vaultAuthority: vaultAuthority(),
vaultToken,
playerToken: await ata(mint, p1.publicKey),
feeToken: feeAccount,
tokenProgram: TOKEN_PROGRAM_ID,
escrowProgram: escrowProgram.programId,
})
.signers([p1])
.rpc(),
"double refund",
);
});

it("rejects settle with < 2 players, double-join, and non-settler", async () => {
Expand Down Expand Up @@ -170,6 +243,10 @@ describe("settlement_game_poker", () => {
// Same player joining twice -> PlayerEntry already initialized.
await expectRevert(join(game, vault, vaultToken, p1, 2), "double join");

// Buy-in amount must exactly match the game stake.
const shortBuyer = Keypair.generate();
await expectRevert(join(game, vault, vaultToken, shortBuyer, 1, STAKE - 1), "short buy-in");

// Add a second player so the game is settleable, then a non-settler tries.
const p2 = Keypair.generate();
await join(game, vault, vaultToken, p2, 1);
Expand All @@ -194,6 +271,11 @@ describe("settlement_game_poker", () => {
.rpc(),
"non-settler settle",
);

// The configured table cap is enforced.
await join(game, vault, vaultToken, Keypair.generate(), 1);
await join(game, vault, vaultToken, Keypair.generate(), 1);
await expectRevert(join(game, vault, vaultToken, Keypair.generate(), 1), "game full");
});

// Regression for the CRITICAL vault-theft finding: a pot vault is bound 1:1 to
Expand Down
29 changes: 29 additions & 0 deletions contracts-sol/tests/settlement_job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ describe("settlement_job", () => {
feeAccount = await ata(mint, payer.publicKey);
});

async function expectJobState(
job: PublicKey,
state: "open" | "delivered" | "disputed" | "resolved" | "refunded",
) {
assert.deepEqual((await jobProgram.account.job.fetch(job)).state, {
[state]: {},
});
}

// Create a bound vault + job, fund it from `client`. Returns the handles.
async function setupJob(label: string, client: Keypair, provider: PublicKey, controller: PublicKey) {
const { vault, vaultToken } = await createVault(jobProgram.programId, mint, feeAccount, label);
Expand Down Expand Up @@ -65,6 +74,7 @@ describe("settlement_job", () => {
.rpc();

assert.equal((await escrowProgram.account.vault.fetch(vault)).deposited.toNumber(), FUND);
await expectJobState(job, "open");
return { vault, vaultToken, job, jobId };
}

Expand All @@ -84,6 +94,7 @@ describe("settlement_job", () => {
.accounts({ job, actor: providerKp.publicKey })
.signers([providerKp])
.rpc();
await expectJobState(job, "delivered");

await jobProgram.methods
.approve()
Expand All @@ -106,6 +117,11 @@ describe("settlement_job", () => {
assert.equal((await tokenBalance(feeAccount)) - feeBefore, BigInt(fee));
const acc = await escrowProgram.account.vault.fetch(vault);
assert.equal(acc.disbursed.toNumber(), FUND);
await expectJobState(job, "resolved");
await expectRevert(
jobProgram.methods.markDelivered().accounts({ job, actor: providerKp.publicKey }).signers([providerKp]).rpc(),
"resolved job cannot be delivered again",
);
});

it("dispute -> controller resolves to client (refund, no rake)", async () => {
Expand All @@ -120,7 +136,9 @@ describe("settlement_job", () => {
const clientBefore = await tokenBalance(clientToken);

await jobProgram.methods.markDelivered().accounts({ job, actor: providerKp.publicKey }).signers([providerKp]).rpc();
await expectJobState(job, "delivered");
await jobProgram.methods.dispute().accounts({ job, actor: client.publicKey }).signers([client]).rpc();
await expectJobState(job, "disputed");

await jobProgram.methods
.resolve(false) // award_provider = false -> client
Expand All @@ -139,13 +157,19 @@ describe("settlement_job", () => {
.rpc();

assert.equal((await tokenBalance(clientToken)) - clientBefore, BigInt(FUND)); // full refund, no fee
await expectJobState(job, "resolved");
await expectRevert(
jobProgram.methods.dispute().accounts({ job, actor: client.publicKey }).signers([client]).rpc(),
"resolved dispute cannot be disputed again",
);
});

it("refund while Open returns funds to client", async () => {
const client = Keypair.generate();
const providerKp = Keypair.generate();
const controller = Keypair.generate();
await fundSol(client.publicKey);
await fundSol(providerKp.publicKey);
const clientToken = await ata(mint, client.publicKey);

const { vault, vaultToken, job } = await setupJob("job-refund", client, providerKp.publicKey, controller.publicKey);
Expand All @@ -168,6 +192,11 @@ describe("settlement_job", () => {
.rpc();

assert.equal((await tokenBalance(clientToken)) - before, BigInt(FUND));
await expectJobState(job, "refunded");
await expectRevert(
jobProgram.methods.markDelivered().accounts({ job, actor: providerKp.publicKey }).signers([providerKp]).rpc(),
"refunded job cannot be delivered",
);
});

it("rejects unauthorized actors", async () => {
Expand Down
3 changes: 3 additions & 0 deletions sdk/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"files": [
"dist"
],
"bin": {
"tinyplace": "./dist/cli.js"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
Expand Down
4 changes: 4 additions & 0 deletions sdk/typescript/src/api/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ export class RegistryApi {
);
}

exportIdentity(name: string): Promise<IdentityExport> {
return this.export(name);
}

async updateProfileVisibility(
name: string,
update: ProfileVisibilityUpdate,
Expand Down
Loading
Loading