Change to spl_token transfer
This commit is contained in:
parent
f83a8142bb
commit
bc16d7d509
|
@ -138,6 +138,7 @@ dependencies = [
|
||||||
"num",
|
"num",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"spl-token",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -22,6 +22,7 @@ overflow-checks = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anchor-lang = { version= "0.24.2", features = ["init-if-needed"]}
|
anchor-lang = { version= "0.24.2", features = ["init-if-needed"]}
|
||||||
anchor-spl = "0.24.2"
|
anchor-spl = "0.24.2"
|
||||||
|
spl-token = "3.3.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
borsh = "0.9.3"
|
borsh = "0.9.3"
|
||||||
|
|
|
@ -41,12 +41,15 @@ pub enum SaleError {
|
||||||
#[msg("SaleEnded")]
|
#[msg("SaleEnded")]
|
||||||
SaleEnded,
|
SaleEnded,
|
||||||
|
|
||||||
#[msg("SaleNotFinished")]
|
|
||||||
SaleNotFinished,
|
|
||||||
|
|
||||||
#[msg("SaleNotAborted")]
|
#[msg("SaleNotAborted")]
|
||||||
SaleNotAborted,
|
SaleNotAborted,
|
||||||
|
|
||||||
|
#[msg("SaleNotAttestable")]
|
||||||
|
SaleNotAttestable,
|
||||||
|
|
||||||
|
#[msg("SaleNotFinished")]
|
||||||
|
SaleNotFinished,
|
||||||
|
|
||||||
#[msg("SaleNotSealed")]
|
#[msg("SaleNotSealed")]
|
||||||
SaleNotSealed,
|
SaleNotSealed,
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub mod anchor_contributor {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use anchor_spl::*;
|
use anchor_spl::*;
|
||||||
|
use spl_token;
|
||||||
|
|
||||||
pub fn create_custodian(ctx: Context<CreateCustodian>) -> Result<()> {
|
pub fn create_custodian(ctx: Context<CreateCustodian>) -> Result<()> {
|
||||||
let custodian = &mut ctx.accounts.custodian;
|
let custodian = &mut ctx.accounts.custodian;
|
||||||
|
@ -69,14 +70,15 @@ pub mod anchor_contributor {
|
||||||
let owner = &ctx.accounts.owner;
|
let owner = &ctx.accounts.owner;
|
||||||
|
|
||||||
//let ata_seeds: &'a [&[u8]] = &[&owner.key().as_ref(), &token::ID.as_ref(), &mint.as_ref()];
|
//let ata_seeds: &'a [&[u8]] = &[&owner.key().as_ref(), &token::ID.as_ref(), &mint.as_ref()];
|
||||||
|
/*
|
||||||
let (ata, bump) = Pubkey::find_program_address(
|
let (ata, bump) = Pubkey::find_program_address(
|
||||||
&[&owner.key().as_ref(), &token::ID.as_ref(), &mint.as_ref()],
|
&[&owner.key().as_ref(), &token::ID.as_ref(), &mint.as_ref()],
|
||||||
&associated_token::AssociatedToken::id(),
|
&associated_token::AssociatedToken::id(),
|
||||||
);
|
);
|
||||||
msg!("ata: {:?}, bump: {:?}", ata, bump);
|
msg!("ata: {:?}, bump: {:?}", ata, bump);
|
||||||
|
*/
|
||||||
|
|
||||||
// spl transfer contribution
|
// spl transfer contribution
|
||||||
/*
|
|
||||||
let ix = spl_token::instruction::transfer(
|
let ix = spl_token::instruction::transfer(
|
||||||
&token::ID,
|
&token::ID,
|
||||||
&ctx.accounts.buyer_ata.key(),
|
&ctx.accounts.buyer_ata.key(),
|
||||||
|
@ -95,8 +97,8 @@ pub mod anchor_contributor {
|
||||||
ctx.accounts.token_program.to_account_info(),
|
ctx.accounts.token_program.to_account_info(),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
*/
|
|
||||||
|
|
||||||
|
/*
|
||||||
token::transfer(
|
token::transfer(
|
||||||
CpiContext::new_with_signer(
|
CpiContext::new_with_signer(
|
||||||
ctx.accounts.token_program.to_account_info(),
|
ctx.accounts.token_program.to_account_info(),
|
||||||
|
@ -109,6 +111,7 @@ pub mod anchor_contributor {
|
||||||
),
|
),
|
||||||
amount,
|
amount,
|
||||||
)?;
|
)?;
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let custodian_bump = ctx.bumps["custodian"];
|
let custodian_bump = ctx.bumps["custodian"];
|
||||||
|
@ -253,7 +256,7 @@ pub mod anchor_contributor {
|
||||||
let refunds = ctx.accounts.buyer.claim_refunds(&sale.totals)?;
|
let refunds = ctx.accounts.buyer.claim_refunds(&sale.totals)?;
|
||||||
let atas = &ctx.remaining_accounts;
|
let atas = &ctx.remaining_accounts;
|
||||||
require!(
|
require!(
|
||||||
atas.len() == sale.totals.len(),
|
atas.len() == 2 * sale.totals.len(),
|
||||||
SaleError::InvalidRemainingAccounts
|
SaleError::InvalidRemainingAccounts
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ impl Sale {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_contributions(&self, block_time: i64) -> Result<Vec<u8>> {
|
pub fn serialize_contributions(&self, block_time: i64) -> Result<Vec<u8>> {
|
||||||
require!(self.is_attestable(block_time), SaleError::SaleNotFinished);
|
require!(self.is_attestable(block_time), SaleError::SaleNotAttestable);
|
||||||
|
|
||||||
let totals = &self.totals;
|
let totals = &self.totals;
|
||||||
let mut attested: Vec<u8> = Vec::with_capacity(
|
let mut attested: Vec<u8> = Vec::with_capacity(
|
||||||
|
@ -211,6 +211,7 @@ impl Sale {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_sale_sealed(&mut self, payload: &[u8]) -> Result<()> {
|
pub fn parse_sale_sealed(&mut self, payload: &[u8]) -> Result<()> {
|
||||||
|
require!(!self.has_ended(), SaleError::SaleEnded);
|
||||||
// check that the payload has at least the number of bytes
|
// check that the payload has at least the number of bytes
|
||||||
// required to define the number of allocations
|
// required to define the number of allocations
|
||||||
require!(
|
require!(
|
||||||
|
@ -296,8 +297,7 @@ impl Sale {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_ended(&self) -> bool {
|
pub fn has_ended(&self) -> bool {
|
||||||
return self.initialized && self.status == SaleStatus::Sealed
|
return self.initialized && self.status != SaleStatus::Active;
|
||||||
|| self.status == SaleStatus::Aborted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_sealed(&self) -> bool {
|
pub fn is_sealed(&self) -> bool {
|
||||||
|
|
|
@ -133,7 +133,6 @@ describe("anchor-contributor", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
describe("Conduct Successful Sale", () => {
|
describe("Conduct Successful Sale", () => {
|
||||||
// global contributions for test
|
// global contributions for test
|
||||||
const contributions = new Map<web3.PublicKey, string[]>();
|
const contributions = new Map<web3.PublicKey, string[]>();
|
||||||
|
@ -194,6 +193,7 @@ describe("anchor-contributor", () => {
|
||||||
let caughtError = false;
|
let caughtError = false;
|
||||||
try {
|
try {
|
||||||
const tx = await contributor.initSale(orchestrator, dummyConductor.initSaleVaa);
|
const tx = await contributor.initSale(orchestrator, dummyConductor.initSaleVaa);
|
||||||
|
console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// pda init should fail
|
// pda init should fail
|
||||||
caughtError = "programErrorStack" in e;
|
caughtError = "programErrorStack" in e;
|
||||||
|
@ -212,9 +212,9 @@ describe("anchor-contributor", () => {
|
||||||
try {
|
try {
|
||||||
const mint = hexToPublicKey(dummyConductor.acceptedTokens[0].address);
|
const mint = hexToPublicKey(dummyConductor.acceptedTokens[0].address);
|
||||||
const tx = await contributor.contribute(buyer, saleId, mint, new BN(amount));
|
const tx = await contributor.contribute(buyer, saleId, mint, new BN(amount));
|
||||||
|
console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//console.log("too early", e);
|
caughtError = verifyErrorMsg(e, "ContributionTooEarly");
|
||||||
caughtError = e.msg == "ContributionTooEarly";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caughtError) {
|
if (!caughtError) {
|
||||||
|
@ -328,10 +328,13 @@ describe("anchor-contributor", () => {
|
||||||
let caughtError = false;
|
let caughtError = false;
|
||||||
try {
|
try {
|
||||||
const tx = await contributor.attestContributions(orchestrator, saleId);
|
const tx = await contributor.attestContributions(orchestrator, saleId);
|
||||||
console.log(tx);
|
console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e.error.errorCode.code);
|
caughtError = verifyErrorMsg(e, "SaleNotAttestable");
|
||||||
caughtError = e.error.errorCode.code == "ContributionTooEarly";
|
}
|
||||||
|
|
||||||
|
if (!caughtError) {
|
||||||
|
throw Error("did not catch expected error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -353,7 +356,19 @@ describe("anchor-contributor", () => {
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
it("Orchestrator Cannot Attest Contributions Again", async () => {
|
it("Orchestrator Cannot Attest Contributions Again", async () => {
|
||||||
expect(false).to.be.true;
|
const saleId = dummyConductor.getSaleId();
|
||||||
|
|
||||||
|
let caughtError = false;
|
||||||
|
try {
|
||||||
|
const tx = await contributor.attestContributions(orchestrator, saleId);
|
||||||
|
console.log("should not happen", tx);
|
||||||
|
} catch (e) {
|
||||||
|
caughtError = verifyErrorMsg(e, "SaleNotAttestable");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caughtError) {
|
||||||
|
throw Error("did not catch expected error");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("User Cannot Contribute After Sale Ended", async () => {
|
it("User Cannot Contribute After Sale Ended", async () => {
|
||||||
|
@ -364,8 +379,9 @@ describe("anchor-contributor", () => {
|
||||||
try {
|
try {
|
||||||
const mint = hexToPublicKey(dummyConductor.acceptedTokens[0].address);
|
const mint = hexToPublicKey(dummyConductor.acceptedTokens[0].address);
|
||||||
const tx = await contributor.contribute(buyer, saleId, mint, amount);
|
const tx = await contributor.contribute(buyer, saleId, mint, amount);
|
||||||
|
console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
caughtError = e.msg == "SaleEnded";
|
caughtError = verifyErrorMsg(e, "SaleEnded");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caughtError) {
|
if (!caughtError) {
|
||||||
|
@ -398,9 +414,9 @@ describe("anchor-contributor", () => {
|
||||||
let caughtError = false;
|
let caughtError = false;
|
||||||
try {
|
try {
|
||||||
const tx = await contributor.sealSale(orchestrator, saleSealedVaa);
|
const tx = await contributor.sealSale(orchestrator, saleSealedVaa);
|
||||||
|
console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//caughtError = e.error.errorCode.code == "SaleEnded";
|
caughtError = verifyErrorMsg(e, "SaleEnded");
|
||||||
console.log(e.error.errorCode.code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caughtError) {
|
if (!caughtError) {
|
||||||
|
@ -425,7 +441,7 @@ describe("anchor-contributor", () => {
|
||||||
expect(false).to.be.true;
|
expect(false).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
describe("Conduct Aborted Sale", () => {
|
describe("Conduct Aborted Sale", () => {
|
||||||
// global contributions for test
|
// global contributions for test
|
||||||
const contributions = new Map<web3.PublicKey, string[]>();
|
const contributions = new Map<web3.PublicKey, string[]>();
|
||||||
|
@ -530,8 +546,9 @@ describe("anchor-contributor", () => {
|
||||||
let caughtError = false;
|
let caughtError = false;
|
||||||
try {
|
try {
|
||||||
const tx = await contributor.abortSale(orchestrator, saleAbortedVaa);
|
const tx = await contributor.abortSale(orchestrator, saleAbortedVaa);
|
||||||
|
console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
caughtError = e.error.errorCode.code == "SaleEnded";
|
caughtError = verifyErrorMsg(e, "SaleEnded");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caughtError) {
|
if (!caughtError) {
|
||||||
|
@ -559,9 +576,9 @@ describe("anchor-contributor", () => {
|
||||||
let caughtError = false;
|
let caughtError = false;
|
||||||
try {
|
try {
|
||||||
//const tx = await contributor.claimRefund(saleId, buyer, acceptedMints);
|
//const tx = await contributor.claimRefund(saleId, buyer, acceptedMints);
|
||||||
|
//console.log("should not happen", tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
caughtError = verifyErrorMsg(e, "BuyerInactive");
|
||||||
caughtError = e.error.errorCode.code == "SaleEnded";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caughtError) {
|
if (!caughtError) {
|
||||||
|
@ -570,3 +587,22 @@ describe("anchor-contributor", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function verifyErrorMsg(e: any, msg: string): boolean {
|
||||||
|
if (e.msg) {
|
||||||
|
const result = e.msg == msg;
|
||||||
|
if (!result) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else if (e.error.errorMessage) {
|
||||||
|
const result = e.error.errorMessage == msg;
|
||||||
|
if (!result) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(e);
|
||||||
|
throw Error("unknown error");
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { getAccount, getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from "@solana
|
||||||
import { findBuyerAccount, findCustodianAccount, findSaleAccount, findSignedVaaAccount, KeyBump } from "./accounts";
|
import { findBuyerAccount, findCustodianAccount, findSaleAccount, findSignedVaaAccount, KeyBump } from "./accounts";
|
||||||
import { getBuyerState, getCustodianState, getSaleState } from "./fetch";
|
import { getBuyerState, getCustodianState, getSaleState } from "./fetch";
|
||||||
import { postVaa } from "./wormhole";
|
import { postVaa } from "./wormhole";
|
||||||
import { getPdaAssociatedTokenAddress } from "./utils";
|
import { getPdaAssociatedTokenAddress, makeWritableAccountMeta } from "./utils";
|
||||||
import { ASSOCIATED_PROGRAM_ID } from "@project-serum/anchor/dist/cjs/utils/token";
|
import { ASSOCIATED_PROGRAM_ID } from "@project-serum/anchor/dist/cjs/utils/token";
|
||||||
|
|
||||||
export class IccoContributor {
|
export class IccoContributor {
|
||||||
|
@ -173,10 +173,10 @@ export class IccoContributor {
|
||||||
const buyerAccount = findBuyerAccount(program.programId, saleId, payer.publicKey);
|
const buyerAccount = findBuyerAccount(program.programId, saleId, payer.publicKey);
|
||||||
const saleAccount = findSaleAccount(program.programId, saleId);
|
const saleAccount = findSaleAccount(program.programId, saleId);
|
||||||
|
|
||||||
const remainingAccounts = [];
|
const remainingAccounts: web3.AccountMeta[] = [];
|
||||||
for (const mint of acceptedMints) {
|
for (const mint of acceptedMints) {
|
||||||
remainingAccounts.push(await getAssociatedTokenAddress(mint, payer.publicKey));
|
remainingAccounts.push(makeWritableAccountMeta(await getAssociatedTokenAddress(mint, payer.publicKey)));
|
||||||
remainingAccounts.push(await getPdaAssociatedTokenAddress(mint, custodian));
|
remainingAccounts.push(makeWritableAccountMeta(await getPdaAssociatedTokenAddress(mint, custodian)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return program.methods
|
return program.methods
|
||||||
|
|
|
@ -38,3 +38,11 @@ export function hexToPublicKey(hexlified: string): web3.PublicKey {
|
||||||
export async function getPdaAssociatedTokenAddress(mint: web3.PublicKey, pda: web3.PublicKey): Promise<web3.PublicKey> {
|
export async function getPdaAssociatedTokenAddress(mint: web3.PublicKey, pda: web3.PublicKey): Promise<web3.PublicKey> {
|
||||||
return getAssociatedTokenAddress(mint, pda, true);
|
return getAssociatedTokenAddress(mint, pda, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeWritableAccountMeta(pubkey: web3.PublicKey): web3.AccountMeta {
|
||||||
|
return {
|
||||||
|
pubkey,
|
||||||
|
isWritable: true,
|
||||||
|
isSigner: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue