chore: migrated devnet program to same pubkey as mainnet (#100)
This commit is contained in:
parent
865da03b59
commit
91080af417
|
@ -106,11 +106,11 @@ jobs:
|
|||
cluster: devnet
|
||||
args:
|
||||
"--url ${{ secrets.DEVNET_RPC_URL }} --clone
|
||||
2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG --clone
|
||||
J4CArpsbrZqu1axqQ4AnrqREs3jwoyA1M5LMiQQmAzB9 --clone
|
||||
CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp --clone
|
||||
BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt --clone
|
||||
FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy"
|
||||
SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f --clone
|
||||
7nYabs9dUhvxYwdTnrWVBL9MYviKSfrEbdWCUbcnwkpF --clone
|
||||
Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk --clone
|
||||
CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd --clone
|
||||
7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie"
|
||||
- name: Run Tests
|
||||
working-directory: javascript/solana.js
|
||||
env:
|
||||
|
|
|
@ -55,10 +55,10 @@ pnpm build
|
|||
|
||||
## Program IDs
|
||||
|
||||
| **Network** | **Program ID** |
|
||||
| ------------ | ---------------------------------------------- |
|
||||
| Mainnet-Beta | `SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f` |
|
||||
| Devnet | `2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG` |
|
||||
| **Network** | **Program ID** |
|
||||
| ------------ | --------------------------------------------- |
|
||||
| Mainnet-Beta | `SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f` |
|
||||
| Devnet | `SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f` |
|
||||
|
||||
See [switchboard.xyz/explorer](https://switchboard.xyz/explorer) for a list of
|
||||
feeds deployed on Solana.
|
||||
|
|
|
@ -31,10 +31,10 @@ export class AnchorWallet implements anchor.Wallet {
|
|||
export default class SwitchboardProgram {
|
||||
/**
|
||||
* Switchboard Devnet Program ID
|
||||
* 2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG
|
||||
* SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f
|
||||
*/
|
||||
public static devnetPid = new anchor.web3.PublicKey(
|
||||
"2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG"
|
||||
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -100,7 +100,7 @@ export class SwitchboardTestContext implements ISwitchboardTestContext {
|
|||
*/
|
||||
static async loadDevnetQueue(
|
||||
provider: anchor.AnchorProvider,
|
||||
queueKey = "F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy",
|
||||
queueKey = "uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX",
|
||||
tokenAmount = 0
|
||||
) {
|
||||
const payerKeypair = (provider.wallet as sbv2.AnchorWallet).payer;
|
||||
|
|
|
@ -55,7 +55,7 @@ describe("Feed tests", () => {
|
|||
// await devnetConnection.confirmTransaction(airdropSignature);
|
||||
switchboard = await SwitchboardTestContext.loadDevnetQueue(
|
||||
provider,
|
||||
"F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy"
|
||||
"uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX"
|
||||
);
|
||||
console.log("devnet detected");
|
||||
return;
|
||||
|
|
|
@ -38,7 +38,7 @@ const program = await loadSwitchboardProgram(
|
|||
const queueAccount = new OracleQueueAccount({
|
||||
program: program,
|
||||
// devnet permissionless queue
|
||||
publicKey: new PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy"),
|
||||
publicKey: new PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX"),
|
||||
});
|
||||
|
||||
const aggregatorAccount = await AggregatorAccount.create(program, {
|
||||
|
|
|
@ -30,10 +30,10 @@ export { SwitchboardDecimal } from "@switchboard-xyz/common";
|
|||
|
||||
/**
|
||||
* Switchboard Devnet Program ID
|
||||
* 2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG
|
||||
* SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f
|
||||
*/
|
||||
export const SBV2_DEVNET_PID = new PublicKey(
|
||||
"2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG"
|
||||
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
|
||||
);
|
||||
/**
|
||||
* Switchboard Mainnet Program ID
|
||||
|
|
|
@ -101,7 +101,7 @@ Then run the following command to create your own feed using the devnet
|
|||
permissionless queue and crank
|
||||
|
||||
```bash
|
||||
export QUEUE_KEY=F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy
|
||||
export QUEUE_KEY=uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX
|
||||
export CRANK_KEY=GN9jjCy2THzZxhYqZETmPM3my8vg4R5JyNkgULddUMa5
|
||||
sbv2 solana aggregator create "$QUEUE_KEY" \
|
||||
--keypair ~/.config/solana/id.json \
|
||||
|
|
|
@ -29,7 +29,7 @@ import path from "path";
|
|||
dotenv.config();
|
||||
|
||||
const DEVNET_PERMISSIONLESS_QUEUE = new PublicKey(
|
||||
"F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy"
|
||||
"uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX"
|
||||
);
|
||||
|
||||
const DEVNET_PERMISSIONLESS_CRANK = new PublicKey(
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
test/data/*-keypair.json
|
||||
test/data/*-keypair.json
|
||||
|
||||
SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f
|
||||
2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG
|
||||
|
|
|
@ -65,7 +65,7 @@ async function main() {
|
|||
'anchor idl fetch -o ./src/idl/mainnet.json SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f --provider.cluster mainnet'
|
||||
);
|
||||
execSync(
|
||||
'anchor idl fetch -o ./src/idl/devnet.json 2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG --provider.cluster devnet'
|
||||
'anchor idl fetch -o ./src/idl/devnet.json SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f --provider.cluster devnet'
|
||||
);
|
||||
|
||||
execSync(
|
||||
|
@ -98,36 +98,40 @@ async function main() {
|
|||
);
|
||||
|
||||
console.log(file);
|
||||
|
||||
// replace BN import
|
||||
execSync(
|
||||
`sed -i '' 's/import BN from \\"bn.js\\"/import { BN } from \\"@switchboard-xyz\\/common\\"/g' ${file}`
|
||||
shell.sed(
|
||||
'-i',
|
||||
'import BN from "bn.js"',
|
||||
'import { BN } from "@switchboard-xyz/common"',
|
||||
file
|
||||
);
|
||||
// replace borsh import
|
||||
execSync(`sed -i '' 's/@project-serum/@coral-xyz/g' ${file}`);
|
||||
shell.sed('-i', '@project-serum', '@coral-xyz', file);
|
||||
// remove PROGRAM_ID import, we will use SwitchboardProgram instead
|
||||
execSync(
|
||||
`sed -i '' 's/import { PROGRAM_ID } from "..\\/programId"/ /g' ${file}`
|
||||
);
|
||||
shell.sed('-i', 'import { PROGRAM_ID } from "../programId"', '', file);
|
||||
// replace PROGRAM_ID with program.programId
|
||||
execSync(`sed -i '' 's/PROGRAM_ID/program.programId/g' ${file}`);
|
||||
shell.sed('-i', 'PROGRAM_ID', 'program.programId', file);
|
||||
// replace Connection with SwitchboardProgram
|
||||
execSync(
|
||||
`sed -i '' 's/c: Connection,/program: SwitchboardProgram,/g' ${file}`
|
||||
);
|
||||
shell.sed('-i', 'c: Connection,', 'program: SwitchboardProgram,', file);
|
||||
// replace c.getAccountInfo with the SwitchboardProgram connection
|
||||
execSync(
|
||||
`sed -i '' 's/c.getAccountInfo/program.connection.getAccountInfo/g' ${file}`
|
||||
shell.sed(
|
||||
'-i',
|
||||
'c.getAccountInfo',
|
||||
'program.connection.getAccountInfo',
|
||||
file
|
||||
);
|
||||
// replace c.getMultipleAccountsInfo with the SwitchboardProgram connection
|
||||
execSync(
|
||||
`sed -i '' 's/c.getMultipleAccountsInfo/program.connection.getMultipleAccountsInfo/g' ${file}`
|
||||
shell.sed(
|
||||
'-i',
|
||||
'c.getMultipleAccountsInfo',
|
||||
'program.connection.getMultipleAccountsInfo',
|
||||
file
|
||||
);
|
||||
|
||||
// add program as first arguement to instructions
|
||||
if (file.includes('/instructions/')) {
|
||||
execSync(
|
||||
`sed -i '' 's/args:/program: SwitchboardProgram, args:/g' ${file}`
|
||||
);
|
||||
shell.sed('-i', 'args:', 'program: SwitchboardProgram, args:', file);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
import * as sbv2 from './src';
|
||||
import { Job, setupOutputDir } from './utils';
|
||||
|
||||
import { clusterApiUrl, Connection } from '@solana/web3.js';
|
||||
// import { backOff } from 'exponential-backoff';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const VERBOSE = process.env.VERBOSE || false;
|
||||
|
||||
const jobMapPath = path.join(
|
||||
__dirname,
|
||||
sbv2.SBV2_MAINNET_PID.toBase58(),
|
||||
'job_map.csv'
|
||||
);
|
||||
|
||||
async function main() {
|
||||
const [oldDirPath, oldFeedDirPath, oldJobDirPath] = setupOutputDir(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
);
|
||||
const [newDirPath, newFeedDirPath, newJobDirPath] = setupOutputDir(
|
||||
sbv2.SBV2_MAINNET_PID.toBase58()
|
||||
);
|
||||
// if (process.argv.length < 3) {
|
||||
// throw new Error(
|
||||
// `Usage: ts-node migrate [job|aggregator] [jobPubkey|aggregatorPubkey]`
|
||||
// );
|
||||
// }
|
||||
|
||||
const devnetConnection = new Connection(
|
||||
process.env.SOLANA_DEVNET_RPC ?? clusterApiUrl('devnet')
|
||||
);
|
||||
console.log(`rpcUrl: ${devnetConnection.rpcEndpoint}`);
|
||||
|
||||
const payer = sbv2.SwitchboardTestContextV2.loadKeypair(
|
||||
'~/switchboard_environments_v2/devnet/upgrade_authority/upgrade_authority.json'
|
||||
);
|
||||
console.log(`payer: ${payer.publicKey.toBase58()}`);
|
||||
|
||||
const newProgram = await sbv2.SwitchboardProgram.load(
|
||||
'devnet',
|
||||
devnetConnection,
|
||||
payer,
|
||||
sbv2.SBV2_MAINNET_PID
|
||||
);
|
||||
|
||||
const jobs = new Map<string, Job>();
|
||||
for (const file of fs.readdirSync(oldJobDirPath)) {
|
||||
const fileName = path.basename(file).replace('.json', '');
|
||||
const jobDef: Job = JSON.parse(
|
||||
fs.readFileSync(path.join(oldJobDirPath, file), 'utf-8')
|
||||
);
|
||||
jobs.set(fileName, jobDef);
|
||||
}
|
||||
|
||||
console.log(`Found ${jobs.size} jobs to migrate`);
|
||||
|
||||
const jobMap = loadJobMap();
|
||||
|
||||
console.log(`Found ${jobMap.size} jobs that have already been migrated`);
|
||||
|
||||
for (const [jobKey, job] of jobs.entries()) {
|
||||
if (jobMap.has(jobKey)) {
|
||||
continue;
|
||||
}
|
||||
const newJobPath = path.join(newJobDirPath, `${jobKey}.json`);
|
||||
if (fs.existsSync(newJobPath)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const [jobAccount] = await sbv2.JobAccount.create(newProgram, {
|
||||
data: new Uint8Array(JSON.parse(job.definition.data)),
|
||||
name: job.definition.name,
|
||||
expiration: job.definition.expiration,
|
||||
});
|
||||
console.log(
|
||||
`${jobKey.padEnd(44, ' ')} -> ${jobAccount.publicKey.toBase58()}`
|
||||
);
|
||||
fs.writeFileSync(
|
||||
newJobPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
newPublicKey: jobAccount.publicKey.toBase58(),
|
||||
oldPublicKey: jobKey,
|
||||
job: job,
|
||||
},
|
||||
undefined,
|
||||
2
|
||||
)
|
||||
);
|
||||
|
||||
jobMap.set(jobKey, jobAccount.publicKey.toBase58());
|
||||
writeJobMap(jobMap);
|
||||
} catch (error) {
|
||||
console.error(`${jobKey} failed, ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// for (const [jobKey, job] of jobs.entries()) {
|
||||
// const jobAccount = new sbv2.JobAccount()
|
||||
// }
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
function writeJobMap(map: Map<string, string>) {
|
||||
const fileString = `oldPubkey, newPubkey\n${Array.from(map.entries())
|
||||
.map(r => r.join(', '))
|
||||
.join('\n')}`;
|
||||
fs.writeFileSync(jobMapPath, fileString);
|
||||
}
|
||||
|
||||
function loadJobMap(): Map<string, string> {
|
||||
if (!fs.existsSync(jobMapPath)) {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
const map = new Map();
|
||||
const fileString = fs.readFileSync(jobMapPath, 'utf-8');
|
||||
const fileLines = fileString.split('\n').slice(1);
|
||||
fileLines.forEach(r => {
|
||||
const [oldPubkey, newPubkey] = r.split(', ');
|
||||
map.set(oldPubkey, newPubkey);
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
|
@ -29,7 +29,7 @@
|
|||
"localnet:up": "npm run local:validator & sleep 20 && npm run test:localnet",
|
||||
"localnet:down": "kill -9 $(pgrep command solana-test-validator) || exit 0",
|
||||
"localnet": "npm run localnet:up; npm run localnet:down",
|
||||
"local:validator": "shx mkdir -p .anchor/test-ledger || true; solana-test-validator -q -r --ledger .anchor/test-ledger --mint $(solana-keygen pubkey ~/.config/solana/id.json) --bind-address 0.0.0.0 --url https://api.devnet.solana.com --rpc-port 8899 --clone 2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG `# programId` --clone J4CArpsbrZqu1axqQ4AnrqREs3jwoyA1M5LMiQQmAzB9 `# programDataAddress` --clone CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp `# idlAddress` --clone BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt `# programState` --clone FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy `# switchboardVault`",
|
||||
"local:validator": "shx mkdir -p .anchor/test-ledger || true; solana-test-validator -q -r --ledger .anchor/test-ledger --mint $(solana-keygen pubkey ~/.config/solana/id.json) --bind-address 0.0.0.0 --url https://api.devnet.solana.com --rpc-port 8899 --clone SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f `# programId` --clone 7nYabs9dUhvxYwdTnrWVBL9MYviKSfrEbdWCUbcnwkpF `# programDataAddress` --clone Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk `# idlAddress` --clone CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd `# programState` --clone 7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie `# switchboardVault`",
|
||||
"local:validator:mainnet": "solana-test-validator -q -r --ledger .anchor/test-ledger --mint $(solana-keygen pubkey ~/.config/solana/id.json) --bind-address 0.0.0.0 --rpc-port 8899 --url https://api.mainnet-beta.solana.com --clone SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f --clone 7nYabs9dUhvxYwdTnrWVBL9MYviKSfrEbdWCUbcnwkpF --clone Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk --clone CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd --clone J7nSEX8ADf3pVVicd6yKy2Skvg8iLePEmkLUisAAaioD",
|
||||
"generate": "node generate-client.js",
|
||||
"build": "shx rm -rf lib || true; tsc -p tsconfig.cjs.json && tsc",
|
||||
|
@ -43,8 +43,8 @@
|
|||
"fix": "gts fix ./src ./test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@coral-xyz/borsh": "^0.26.0",
|
||||
"@coral-xyz/anchor": "^0.27.0",
|
||||
"@coral-xyz/borsh": "^0.27.0",
|
||||
"@solana/spl-token": "^0.3.6",
|
||||
"@solana/web3.js": "^1.73.0",
|
||||
"@switchboard-xyz/common": "^2.1.33",
|
||||
|
@ -62,6 +62,7 @@
|
|||
"chai": "^4.3.7",
|
||||
"chalk": "^4.1.2",
|
||||
"eslint": "^8.35.0",
|
||||
"exponential-backoff": "^3.1.1",
|
||||
"gts": "^3.1.1",
|
||||
"mocha": "^10.1.0",
|
||||
"shelljs": "^0.8.5",
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
import * as sbv2 from './src';
|
||||
import { jsonReplacers, setupOutputDir } from './utils';
|
||||
|
||||
import * as anchor from '@coral-xyz/anchor';
|
||||
import * as spl from '@solana/spl-token';
|
||||
import {
|
||||
clusterApiUrl,
|
||||
Connection,
|
||||
LAMPORTS_PER_SOL,
|
||||
PublicKey,
|
||||
} from '@solana/web3.js';
|
||||
import { OracleJob, toUtf8 } from '@switchboard-xyz/common';
|
||||
// import { backOff } from 'exponential-backoff';
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import path from 'path';
|
||||
|
||||
const LEASE_THRESHOLD = (10 * 12500) / LAMPORTS_PER_SOL; // must have at least 10 queue rewards in the lease to migrate
|
||||
|
||||
const VERBOSE = process.env.VERBOSE || false;
|
||||
|
||||
interface JobDefinition {
|
||||
account: sbv2.JobAccount;
|
||||
data: sbv2.types.JobAccountData;
|
||||
oracleJob: OracleJob;
|
||||
}
|
||||
|
||||
interface AggregatorDefinition {
|
||||
account: sbv2.AggregatorAccount;
|
||||
data: sbv2.types.AggregatorAccountData;
|
||||
historyBufferLength?: number;
|
||||
// permissions
|
||||
permissionAccount: sbv2.PermissionAccount;
|
||||
permissions: sbv2.types.PermissionAccountData;
|
||||
// lease
|
||||
leaseAccount: sbv2.LeaseAccount;
|
||||
lease: sbv2.types.LeaseAccountData;
|
||||
// jobs
|
||||
jobs: Array<JobDefinition>;
|
||||
}
|
||||
|
||||
type AggregatorDefinitionWithLeaseBalance = AggregatorDefinition & {
|
||||
balance: number;
|
||||
};
|
||||
|
||||
async function main() {
|
||||
const [dirPath, feedDirPath, jobDirPath] = setupOutputDir(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
);
|
||||
|
||||
const devnetConnection = new Connection(
|
||||
process.env.SOLANA_DEVNET_RPC ?? clusterApiUrl('devnet')
|
||||
);
|
||||
console.log(`rpcUrl: ${devnetConnection.rpcEndpoint}`);
|
||||
|
||||
const payer = sbv2.SwitchboardTestContextV2.loadKeypair(
|
||||
'~/.config/solana/id.json'
|
||||
);
|
||||
console.log(`payer: ${payer.publicKey.toBase58()}`);
|
||||
|
||||
const oldProgram = await sbv2.SwitchboardProgram.load(
|
||||
'devnet',
|
||||
devnetConnection,
|
||||
payer,
|
||||
new PublicKey('2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG')
|
||||
);
|
||||
const oldProgramAccounts = await oldProgram.getProgramAccounts();
|
||||
|
||||
const [queueAccount, queue] = await sbv2.QueueAccount.load(
|
||||
oldProgram,
|
||||
sbv2.SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE
|
||||
);
|
||||
|
||||
// const newProgram = await sbv2.SwitchboardProgram.load(
|
||||
// 'devnet',
|
||||
// devnetConnection,
|
||||
// payer,
|
||||
// sbv2.SBV2_MAINNET_PID
|
||||
// );
|
||||
// const newProgramAccounts = await newProgram.getProgramAccounts();
|
||||
|
||||
// load all devnet permissionless aggregators
|
||||
const allAggregators = Array.from(oldProgramAccounts.aggregators.entries());
|
||||
console.log(`Found ${allAggregators.length} aggregators on old programId`);
|
||||
|
||||
const permissionlessAggregators = allAggregators.filter(
|
||||
([aggregatorKey, aggregator]) =>
|
||||
aggregator.queuePubkey.equals(
|
||||
sbv2.SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
`Found ${permissionlessAggregators.length} aggregators on permissionless queue`
|
||||
);
|
||||
|
||||
const aggregators = new Map<string, AggregatorDefinition>();
|
||||
|
||||
for (const [aggregatorKey, aggregator] of permissionlessAggregators) {
|
||||
// check permission account exists
|
||||
const [permissionAccount] = sbv2.PermissionAccount.fromSeed(
|
||||
oldProgram,
|
||||
queue.authority,
|
||||
queueAccount.publicKey,
|
||||
new PublicKey(aggregatorKey)
|
||||
);
|
||||
const permissions = oldProgramAccounts.permissions.get(
|
||||
permissionAccount.publicKey.toBase58()
|
||||
);
|
||||
if (!permissions) {
|
||||
if (VERBOSE) {
|
||||
console.error(`No permissions found for aggregator ${aggregatorKey}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// check lease account exists and has an active balance
|
||||
const [leaseAccount] = sbv2.LeaseAccount.fromSeed(
|
||||
oldProgram,
|
||||
sbv2.SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE,
|
||||
new PublicKey(aggregatorKey)
|
||||
);
|
||||
const lease = oldProgramAccounts.leases.get(
|
||||
leaseAccount.publicKey.toBase58()
|
||||
);
|
||||
if (!lease) {
|
||||
if (VERBOSE) {
|
||||
console.error(`No lease found for aggregator ${aggregatorKey}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const jobPubkeys = aggregator.jobPubkeysData.slice(
|
||||
0,
|
||||
aggregator.jobPubkeysSize
|
||||
);
|
||||
const aggregatorJobs: Array<JobDefinition> = [];
|
||||
for (const job of jobPubkeys) {
|
||||
const jobData = oldProgramAccounts.jobs.get(job.toBase58());
|
||||
if (!jobData) {
|
||||
if (VERBOSE) {
|
||||
console.error(`Failed to find jobData for ${job}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let oracleJob: OracleJob | undefined = undefined;
|
||||
try {
|
||||
oracleJob = OracleJob.decodeDelimited(jobData.data);
|
||||
} catch (error) {
|
||||
if (VERBOSE) {
|
||||
console.error(`Failed to decode job ${job.toBase58()}, ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!oracleJob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const jobDefinition = {
|
||||
account: new sbv2.JobAccount(oldProgram, job),
|
||||
data: jobData,
|
||||
oracleJob: oracleJob,
|
||||
};
|
||||
|
||||
aggregatorJobs.push(jobDefinition);
|
||||
}
|
||||
|
||||
let historyBufferLength: number | undefined = undefined;
|
||||
if (!aggregator.historyBuffer.equals(PublicKey.default)) {
|
||||
const buffer = oldProgramAccounts.buffers.get(
|
||||
aggregator.historyBuffer.toBase58()
|
||||
);
|
||||
if (buffer) {
|
||||
historyBufferLength = Math.ceil((buffer.byteLength - 8) / 28);
|
||||
} else {
|
||||
console.error(
|
||||
`Failed to fetch history buffer for aggregator ${aggregatorKey} (${aggregator.historyBuffer})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
aggregators.set(aggregatorKey, {
|
||||
account: new sbv2.AggregatorAccount(
|
||||
oldProgram,
|
||||
new PublicKey(aggregatorKey)
|
||||
),
|
||||
data: aggregator,
|
||||
historyBufferLength,
|
||||
permissionAccount,
|
||||
permissions,
|
||||
leaseAccount,
|
||||
lease,
|
||||
jobs: aggregatorJobs,
|
||||
});
|
||||
}
|
||||
|
||||
// fetch all lease balances in batches of 100
|
||||
const leaseBalanceMap = new Map<string, number>();
|
||||
const allLeaseEscrows: Array<PublicKey> = Array.from(
|
||||
aggregators.entries()
|
||||
).map(([aggregatorKey, aggregator]) => aggregator.lease.escrow);
|
||||
const leaseBatches: Array<Array<PublicKey>> = _.chunk(allLeaseEscrows, 100);
|
||||
for await (const batch of leaseBatches) {
|
||||
const leaseEscrowAccountInfos = await anchor.utils.rpc.getMultipleAccounts(
|
||||
oldProgram.connection,
|
||||
batch
|
||||
);
|
||||
for (const leaseEscrowAccountInfo of leaseEscrowAccountInfos) {
|
||||
try {
|
||||
const account = spl.AccountLayout.decode(
|
||||
leaseEscrowAccountInfo!.account.data!
|
||||
);
|
||||
|
||||
const leaseBalance =
|
||||
Number.parseInt(account.amount.toString()) / LAMPORTS_PER_SOL;
|
||||
|
||||
leaseBalanceMap.set(
|
||||
leaseEscrowAccountInfo!.publicKey.toBase58(),
|
||||
leaseBalance
|
||||
);
|
||||
} catch (error) {
|
||||
if (VERBOSE) {
|
||||
console.error(`Failed to fetch lease escrow balance`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const aggregatorToMigrate = new Map<
|
||||
string,
|
||||
AggregatorDefinitionWithLeaseBalance
|
||||
>();
|
||||
const jobsToMigrate = new Map<string, JobDefinition>();
|
||||
for (const [aggregatorKey, aggregator] of aggregators.entries()) {
|
||||
const leaseBalance = leaseBalanceMap.get(
|
||||
aggregator.lease.escrow.toBase58()
|
||||
);
|
||||
if (!leaseBalance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (leaseBalance < LEASE_THRESHOLD) {
|
||||
console.error(
|
||||
`Lease is below threshold for aggregator ${aggregatorKey}, lease balance = ${leaseBalance}`
|
||||
);
|
||||
continue;
|
||||
} else {
|
||||
aggregatorToMigrate.set(aggregatorKey, {
|
||||
...aggregator,
|
||||
balance: leaseBalance,
|
||||
});
|
||||
for (const job of aggregator.jobs) {
|
||||
jobsToMigrate.set(job.account.publicKey.toBase58(), job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Found ${aggregatorToMigrate.size} aggregators to migrate`);
|
||||
|
||||
console.log(`Found ${jobsToMigrate.size} jobs to migrate`);
|
||||
|
||||
const jobPubkeys: Array<string> = [];
|
||||
for (const [jobKey, job] of jobsToMigrate.entries()) {
|
||||
try {
|
||||
const jobInitDefinition = {
|
||||
data: `[${new Uint8Array(job.data.data)}]`,
|
||||
name: toUtf8(job.data.name),
|
||||
authority: job.data.authority.toBase58(),
|
||||
expiration: job.data.expiration.toNumber(),
|
||||
variables: undefined,
|
||||
};
|
||||
const fileString = JSON.stringify(
|
||||
{
|
||||
publicKey: jobKey,
|
||||
definition: jobInitDefinition,
|
||||
data: job,
|
||||
},
|
||||
jsonReplacers,
|
||||
2
|
||||
);
|
||||
if (fileString.includes('twapTask')) {
|
||||
const twapTaskPath = path.join(dirPath, 'twapJobs');
|
||||
fs.mkdirSync(twapTaskPath, { recursive: true });
|
||||
fs.writeFileSync(path.join(twapTaskPath, `${jobKey}.json`), fileString);
|
||||
} else {
|
||||
fs.writeFileSync(path.join(jobDirPath, `${jobKey}.json`), fileString);
|
||||
}
|
||||
|
||||
jobPubkeys.push(jobKey);
|
||||
} catch (error) {}
|
||||
}
|
||||
fs.writeFileSync(path.join(dirPath, 'jobs.txt'), jobPubkeys.join('\n'));
|
||||
|
||||
const aggregatorPubkeys: Array<string> = [];
|
||||
for (const [aggregatorKey, aggregator] of aggregatorToMigrate.entries()) {
|
||||
try {
|
||||
const jobs: Array<{ pubkey: string; weight: number }> = [];
|
||||
for (const [i, job] of aggregator.data.jobPubkeysData
|
||||
.slice(0, aggregator.data.jobPubkeysSize)
|
||||
.entries()) {
|
||||
jobs.push({
|
||||
pubkey: job.toBase58(),
|
||||
weight:
|
||||
aggregator.data.jobWeights.length >= i
|
||||
? aggregator.data.jobWeights[i] ?? 1
|
||||
: 1,
|
||||
});
|
||||
}
|
||||
|
||||
const aggregatorInitDefinition = {
|
||||
name: toUtf8(aggregator.data.name),
|
||||
metadata: toUtf8(aggregator.data.metadata),
|
||||
batchSize: aggregator.data.oracleRequestBatchSize,
|
||||
minRequiredOracleResults: aggregator.data.minOracleResults,
|
||||
minRequiredJobResults: aggregator.data.minJobResults,
|
||||
minUpdateDelaySeconds: aggregator.data.minUpdateDelaySeconds,
|
||||
startAfter: aggregator.data.startAfter.toNumber(),
|
||||
varianceThreshold: aggregator.data.varianceThreshold.toBig().toNumber(),
|
||||
forceReportPeriod: aggregator.data.forceReportPeriod.toNumber(),
|
||||
expiration: aggregator.data.expiration.toNumber(),
|
||||
disableCrank: aggregator.data.disableCrank,
|
||||
authority: aggregator.data.authority.toBase58(),
|
||||
historyLimit: aggregator.historyBufferLength,
|
||||
slidingWindow:
|
||||
aggregator.data.resolutionMode.kind === 'ModeSlidingResolution',
|
||||
basePriorityFee: aggregator.data.basePriorityFee,
|
||||
priorityFeeBump: aggregator.data.priorityFeeBump,
|
||||
priorityFeeBumpPeriod: aggregator.data.priorityFeeBumpPeriod,
|
||||
maxPriorityFeeMultiplier: aggregator.data.maxPriorityFeeMultiplier,
|
||||
jobs: jobs,
|
||||
pushCrank: !aggregator.data.crankPubkey.equals(PublicKey.default),
|
||||
};
|
||||
fs.writeFileSync(
|
||||
path.join(feedDirPath, `${aggregatorKey}.json`),
|
||||
JSON.stringify(
|
||||
{
|
||||
publicKey: aggregatorKey,
|
||||
definition: aggregatorInitDefinition,
|
||||
data: aggregator,
|
||||
},
|
||||
jsonReplacers,
|
||||
2
|
||||
)
|
||||
);
|
||||
aggregatorPubkeys.push(aggregatorKey);
|
||||
} catch (error) {}
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(dirPath, 'aggregators.txt'),
|
||||
aggregatorPubkeys.join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
});
|
|
@ -0,0 +1,220 @@
|
|||
import * as sbv2 from './src';
|
||||
import { TransactionObject } from './src';
|
||||
|
||||
import {
|
||||
AccountMeta,
|
||||
clusterApiUrl,
|
||||
Connection,
|
||||
PublicKey,
|
||||
} from '@solana/web3.js';
|
||||
// import { backOff } from 'exponential-backoff';
|
||||
import chalk from 'chalk';
|
||||
import _ from 'lodash';
|
||||
|
||||
export const CHECK_ICON = chalk.green('\u2714 ');
|
||||
|
||||
export const FAILED_ICON = chalk.red('\u2717 ');
|
||||
|
||||
const VERBOSE = process.env.VERBOSE || false;
|
||||
|
||||
const BATCH_SIZE = 20;
|
||||
|
||||
async function main() {
|
||||
const devnetConnection = new Connection(
|
||||
process.env.SOLANA_DEVNET_RPC ?? clusterApiUrl('devnet')
|
||||
);
|
||||
console.log(`rpcUrl: ${devnetConnection.rpcEndpoint}`);
|
||||
|
||||
const payer = sbv2.SwitchboardTestContextV2.loadKeypair(
|
||||
'~/.config/solana/id.json'
|
||||
);
|
||||
console.log(`payer: ${payer.publicKey.toBase58()}`);
|
||||
|
||||
const oldProgram = await sbv2.SwitchboardProgram.load(
|
||||
'devnet',
|
||||
devnetConnection,
|
||||
payer,
|
||||
new PublicKey('2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG')
|
||||
);
|
||||
|
||||
const programAccounts = await oldProgram.getProgramAccounts();
|
||||
const remainingAccounts: Array<AccountMeta> = [];
|
||||
|
||||
for (const [accountKey, accountData] of Array.from(
|
||||
programAccounts.permissions.entries()
|
||||
)) {
|
||||
if (accountData.bump === 0) {
|
||||
remainingAccounts.push({
|
||||
pubkey: new PublicKey(accountKey),
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [accountKey, accountData] of Array.from(
|
||||
programAccounts.leases.entries()
|
||||
)) {
|
||||
if (accountData.bump === 0) {
|
||||
remainingAccounts.push({
|
||||
pubkey: new PublicKey(accountKey),
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [accountKey, accountData] of Array.from(
|
||||
programAccounts.oracles.entries()
|
||||
)) {
|
||||
if (accountData.bump === 0) {
|
||||
remainingAccounts.push({
|
||||
pubkey: new PublicKey(accountKey),
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingAccounts.length === 0) {
|
||||
console.log(`${CHECK_ICON} ALL BUMPS SET`);
|
||||
}
|
||||
|
||||
const batches = _.chunk(remainingAccounts, BATCH_SIZE);
|
||||
|
||||
console.log(`Found ${remainingAccounts.length} bumps to set`);
|
||||
console.log(`Num Batches: ${batches.length}`);
|
||||
|
||||
for (const batch of batches) {
|
||||
try {
|
||||
const setBumpsIxn = sbv2.types.setBumps(
|
||||
oldProgram,
|
||||
{
|
||||
params: {
|
||||
stateBump: oldProgram.programState.bump,
|
||||
},
|
||||
},
|
||||
{
|
||||
state: oldProgram.programState.publicKey,
|
||||
}
|
||||
);
|
||||
setBumpsIxn.keys.push(...batch);
|
||||
|
||||
const setBumpsTxn = new TransactionObject(
|
||||
oldProgram.walletPubkey,
|
||||
[setBumpsIxn],
|
||||
[],
|
||||
undefined
|
||||
);
|
||||
|
||||
const setBumpsSignature = await oldProgram.signAndSend(setBumpsTxn, {
|
||||
skipConfrimation: true,
|
||||
});
|
||||
console.log(`${CHECK_ICON} ${setBumpsSignature}`);
|
||||
} catch (error) {
|
||||
console.log(`${FAILED_ICON} failed to send batch, ${error}`);
|
||||
for (const key of batch) {
|
||||
console.log(`\t - ${key.pubkey.toBase58()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const [aggregatorAccount, aggregator] = await AggregatorAccount.load(
|
||||
// oldProgram,
|
||||
// '3pGhzWwW9AjnF2iRuRWvK2dQAt6YKLP4UJsibYBpozYS'
|
||||
// );
|
||||
|
||||
// console.log(
|
||||
// `${'AggregatorAccount'.padEnd(20, ' ')}: ${aggregatorAccount.publicKey}`
|
||||
// );
|
||||
|
||||
// const [queueAccount, queue] = await QueueAccount.load(
|
||||
// oldProgram,
|
||||
// aggregator.queuePubkey
|
||||
// );
|
||||
// const accounts = aggregatorAccount.getAccounts(queueAccount, queue.authority);
|
||||
|
||||
// const initialPermissions = await accounts.permissionAccount.loadData();
|
||||
// const initialLease = await accounts.leaseAccount.loadData();
|
||||
// console.log(
|
||||
// `${'PermissionAccount'.padEnd(20, ' ')}: ${
|
||||
// accounts.permissionAccount.publicKey
|
||||
// } (${initialPermissions.bump} => ${accounts.permissionBump})`
|
||||
// );
|
||||
// console.log(
|
||||
// `${'LeaseAccount'.padEnd(20, ' ')}: ${
|
||||
// accounts.permissionAccount.publicKey
|
||||
// } (${initialLease.bump} => ${accounts.leaseBump})`
|
||||
// );
|
||||
|
||||
// const setBumpsIxn = sbv2.types.setBumps(
|
||||
// oldProgram,
|
||||
// {
|
||||
// params: {
|
||||
// stateBump: oldProgram.programState.bump,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// state: oldProgram.programState.publicKey,
|
||||
// }
|
||||
// );
|
||||
|
||||
// setBumpsIxn.keys.push({
|
||||
// pubkey: accounts.permissionAccount.publicKey,
|
||||
// isSigner: false,
|
||||
// isWritable: true,
|
||||
// });
|
||||
// setBumpsIxn.keys.push({
|
||||
// pubkey: accounts.leaseAccount.publicKey,
|
||||
// isSigner: false,
|
||||
// isWritable: true,
|
||||
// });
|
||||
|
||||
// const setBumpsTxn = new TransactionObject(
|
||||
// oldProgram.walletPubkey,
|
||||
// [setBumpsIxn],
|
||||
// [],
|
||||
// undefined
|
||||
// );
|
||||
|
||||
// const setBumpsSignature = await oldProgram.signAndSend(setBumpsTxn);
|
||||
// console.log(`txnSignature: ${setBumpsSignature}`);
|
||||
|
||||
// await sleep(2000);
|
||||
|
||||
// const finalPermissionsAccountInfo =
|
||||
// await oldProgram.connection.getAccountInfo(
|
||||
// accounts.permissionAccount.publicKey,
|
||||
// 'processed'
|
||||
// );
|
||||
// const finalPermissions = sbv2.types.PermissionAccountData.decode(
|
||||
// finalPermissionsAccountInfo!.data
|
||||
// );
|
||||
// const finalLeaseAccountInfo = await oldProgram.connection.getAccountInfo(
|
||||
// accounts.leaseAccount.publicKey,
|
||||
// 'processed'
|
||||
// );
|
||||
// const finalLease = sbv2.types.LeaseAccountData.decode(
|
||||
// finalLeaseAccountInfo!.data
|
||||
// );
|
||||
|
||||
// const parsedTxnLogs =
|
||||
// await queueAccount.program.connection.getParsedTransaction(
|
||||
// setBumpsSignature,
|
||||
// { commitment: 'confirmed' }
|
||||
// );
|
||||
// console.log(JSON.stringify(parsedTxnLogs?.meta?.logMessages, undefined, 2));
|
||||
|
||||
// assert(
|
||||
// finalPermissions.bump === accounts.permissionBump,
|
||||
// `PermissionAccount bump mismatch, expected ${accounts.permissionBump}, received ${finalPermissions.bump}`
|
||||
// );
|
||||
// assert(
|
||||
// finalLease.bump === accounts.leaseBump,
|
||||
// `LeaseAccount bump mismatch, expected ${accounts.leaseBump}, received ${finalLease.bump}`
|
||||
// );
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error(error);
|
||||
});
|
|
@ -51,6 +51,7 @@ import {
|
|||
SendOptions,
|
||||
Transaction,
|
||||
TransactionSignature,
|
||||
VersionedTransaction,
|
||||
} from '@solana/web3.js';
|
||||
import { OracleJob } from '@switchboard-xyz/common';
|
||||
|
||||
|
@ -67,7 +68,7 @@ export const DEFAULT_SEND_TRANSACTION_OPTIONS: SendTransactionOptions = {
|
|||
* Switchboard Devnet Program ID
|
||||
*/
|
||||
export const SBV2_DEVNET_PID = new PublicKey(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -88,11 +89,10 @@ export const getSwitchboardProgramId = (
|
|||
cluster: Cluster | 'localnet'
|
||||
): PublicKey => {
|
||||
switch (cluster) {
|
||||
case 'mainnet-beta':
|
||||
return SBV2_MAINNET_PID;
|
||||
case 'localnet':
|
||||
case 'devnet':
|
||||
return SBV2_DEVNET_PID;
|
||||
case 'mainnet-beta':
|
||||
return SBV2_MAINNET_PID;
|
||||
case 'testnet':
|
||||
default:
|
||||
throw new Error(`Switchboard PID not found for cluster (${cluster})`);
|
||||
|
@ -291,15 +291,7 @@ export class SwitchboardProgram {
|
|||
? 'devnet'
|
||||
: 'localnet';
|
||||
|
||||
let pid = programId;
|
||||
if (!pid) {
|
||||
pid =
|
||||
programId ?? cluster === 'mainnet-beta'
|
||||
? SBV2_MAINNET_PID
|
||||
: cluster === 'devnet'
|
||||
? SBV2_DEVNET_PID
|
||||
: SBV2_DEVNET_PID;
|
||||
}
|
||||
const pid = programId ?? SBV2_MAINNET_PID;
|
||||
|
||||
const programAccountInfo = await connection.getAccountInfo(pid);
|
||||
if (programAccountInfo === null) {
|
||||
|
@ -731,22 +723,46 @@ export class SwitchboardProgram {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a transaction object is a VersionedTransaction or not
|
||||
*
|
||||
* @param tx
|
||||
* @returns bool
|
||||
*/
|
||||
export const isVersionedTransaction = (tx): tx is VersionedTransaction => {
|
||||
return 'version' in tx;
|
||||
};
|
||||
|
||||
export class AnchorWallet implements anchor.Wallet {
|
||||
constructor(readonly payer: Keypair) {
|
||||
this.payer = payer;
|
||||
}
|
||||
constructor(readonly payer: Keypair) {}
|
||||
|
||||
get publicKey(): PublicKey {
|
||||
return this.payer.publicKey;
|
||||
}
|
||||
private sign = (txn: Transaction): Transaction => {
|
||||
txn.partialSign(this.payer);
|
||||
return txn;
|
||||
};
|
||||
async signTransaction(txn: Transaction) {
|
||||
return this.sign(txn);
|
||||
|
||||
async signTransaction<T extends Transaction | VersionedTransaction>(
|
||||
tx: T
|
||||
): Promise<T> {
|
||||
if (isVersionedTransaction(tx)) {
|
||||
tx.sign([this.payer]);
|
||||
} else {
|
||||
tx.partialSign(this.payer);
|
||||
}
|
||||
|
||||
return tx;
|
||||
}
|
||||
async signAllTransactions(txns: Transaction[]) {
|
||||
return txns.map(this.sign);
|
||||
|
||||
async signAllTransactions<T extends Transaction | VersionedTransaction>(
|
||||
txs: T[]
|
||||
): Promise<T[]> {
|
||||
return txs.map(t => {
|
||||
if (isVersionedTransaction(t)) {
|
||||
t.sign([this.payer]);
|
||||
} else {
|
||||
t.partialSign(this.payer);
|
||||
}
|
||||
return t;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,14 +63,14 @@ import { QueueDataBuffer } from './queueDataBuffer';
|
|||
import { VrfAccount } from './vrfAccount';
|
||||
|
||||
export const BUFFER_DISCRIMINATOR = Buffer.from([
|
||||
42,
|
||||
55,
|
||||
46,
|
||||
46,
|
||||
45,
|
||||
52,
|
||||
78,
|
||||
78, // BUFFERxx
|
||||
66,
|
||||
85,
|
||||
70,
|
||||
70,
|
||||
69,
|
||||
82,
|
||||
120,
|
||||
120, // BUFFERxx
|
||||
]);
|
||||
|
||||
export type SwitchboardAccountType =
|
||||
|
|
|
@ -163,7 +163,7 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
|
|||
}
|
||||
|
||||
public get slidingWindowKey(): PublicKey {
|
||||
return anchor.utils.publicKey.findProgramAddressSync(
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from('SlidingResultAccountData'), this.publicKey.toBytes()],
|
||||
this.program.programId
|
||||
)[0];
|
||||
|
@ -850,6 +850,12 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
|
|||
`Failed to fetch account data for job ${j?.publicKey}`
|
||||
);
|
||||
}
|
||||
if (!j.account.owner.equals(this.program.programId)) {
|
||||
throw new errors.IncorrectOwner(
|
||||
this.program.programId,
|
||||
j.account.owner
|
||||
);
|
||||
}
|
||||
const jobAccount = new JobAccount(this.program, j.publicKey);
|
||||
const jobState: types.JobAccountData = this.program.coder.decode(
|
||||
'JobAccountData',
|
||||
|
@ -1661,14 +1667,14 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
|
|||
},
|
||||
permission: {
|
||||
publicKey: accounts.permission.publicKey,
|
||||
bump: accounts.permission.bump,
|
||||
...accounts.permission.data.toJSON(),
|
||||
bump: accounts.permission.bump,
|
||||
},
|
||||
lease: {
|
||||
publicKey: accounts.lease.publicKey,
|
||||
...accounts.lease.data.toJSON(),
|
||||
bump: accounts.lease.bump,
|
||||
balance: accounts.lease.balance,
|
||||
...accounts.lease.data.toJSON(),
|
||||
},
|
||||
jobs: accounts.jobs.map(j => {
|
||||
return {
|
||||
|
@ -2043,6 +2049,132 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
|
|||
const txnSignatures = await program.signAndSendAll(txns);
|
||||
return txnSignatures;
|
||||
}
|
||||
|
||||
public async closeInstructions(
|
||||
payer: PublicKey,
|
||||
params?: {
|
||||
authority?: Keypair;
|
||||
tokenWallet?: PublicKey;
|
||||
},
|
||||
opts?: TransactionObjectOptions
|
||||
): Promise<TransactionObject> {
|
||||
if (this.program.cluster === 'mainnet-beta') {
|
||||
throw new Error(
|
||||
`Aggregators can only be closed with the devnet version of Switchboard`
|
||||
);
|
||||
}
|
||||
const [tokenWallet, tokenWalletInit] = params?.tokenWallet
|
||||
? [params.tokenWallet, undefined]
|
||||
: await this.program.mint.getOrCreateWrappedUserInstructions(payer, {
|
||||
fundUpTo: 0,
|
||||
});
|
||||
|
||||
const aggregator = await this.loadData();
|
||||
const [queueAccount, queue] = await QueueAccount.load(
|
||||
this.program,
|
||||
aggregator.queuePubkey
|
||||
);
|
||||
|
||||
const {
|
||||
permissionAccount,
|
||||
permissionBump,
|
||||
leaseAccount,
|
||||
leaseBump,
|
||||
leaseEscrow,
|
||||
} = this.getAccounts(queueAccount, queue.authority);
|
||||
|
||||
const crankPubkey: PublicKey | null = aggregator.crankPubkey.equals(
|
||||
PublicKey.default
|
||||
)
|
||||
? null
|
||||
: aggregator.crankPubkey;
|
||||
let dataBuffer: PublicKey | null = null;
|
||||
if (crankPubkey !== null) {
|
||||
const [crankAccount, crank] = await CrankAccount.load(
|
||||
this.program,
|
||||
crankPubkey
|
||||
);
|
||||
dataBuffer = crank.dataBuffer;
|
||||
}
|
||||
|
||||
const closeIxn = await (
|
||||
(this.program as any)._program as anchor.Program
|
||||
).methods
|
||||
.aggregatorClose({
|
||||
stateBump: this.program.programState.bump,
|
||||
permissionBump: permissionBump,
|
||||
leaseBump: leaseBump,
|
||||
})
|
||||
.accounts(
|
||||
crankPubkey !== null && dataBuffer !== null
|
||||
? {
|
||||
authority: aggregator.authority,
|
||||
aggregator: this.publicKey,
|
||||
permission: permissionAccount.publicKey,
|
||||
lease: leaseAccount.publicKey,
|
||||
escrow: leaseEscrow,
|
||||
oracleQueue: queueAccount.publicKey,
|
||||
queueAuthority: queue.authority,
|
||||
programState: queueAccount.program.programState.publicKey,
|
||||
solDest: payer,
|
||||
escrowDest: tokenWallet,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
crank: crankPubkey,
|
||||
dataBuffer: dataBuffer,
|
||||
}
|
||||
: ({
|
||||
authority: aggregator.authority,
|
||||
aggregator: this.publicKey,
|
||||
permission: permissionAccount.publicKey,
|
||||
lease: leaseAccount.publicKey,
|
||||
escrow: leaseEscrow,
|
||||
oracleQueue: queueAccount.publicKey,
|
||||
queueAuthority: queue.authority,
|
||||
programState: queueAccount.program.programState.publicKey,
|
||||
solDest: payer,
|
||||
escrowDest: tokenWallet,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
crank: null,
|
||||
dataBuffer: null,
|
||||
} as any) // compiler expects all types to be pubkeys
|
||||
)
|
||||
.instruction();
|
||||
|
||||
const closeTxn = tokenWalletInit
|
||||
? tokenWalletInit.add(
|
||||
closeIxn,
|
||||
params?.authority ? [params.authority] : undefined
|
||||
)
|
||||
: new TransactionObject(
|
||||
payer,
|
||||
[closeIxn],
|
||||
params?.authority ? [params.authority] : []
|
||||
);
|
||||
|
||||
// add txn options
|
||||
return new TransactionObject(
|
||||
closeTxn.payer,
|
||||
closeTxn.ixns,
|
||||
closeTxn.signers,
|
||||
opts
|
||||
);
|
||||
}
|
||||
|
||||
public async close(
|
||||
params?: {
|
||||
authority?: Keypair;
|
||||
tokenWallet?: PublicKey;
|
||||
},
|
||||
opts?: TransactionObjectOptions
|
||||
): Promise<TransactionSignature> {
|
||||
const closeTxn = await this.closeInstructions(
|
||||
this.program.walletPubkey,
|
||||
params,
|
||||
opts
|
||||
);
|
||||
const txnSignature = await this.program.signAndSend(closeTxn);
|
||||
return txnSignature;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -464,8 +464,8 @@ export class BufferRelayerAccount extends Account<types.BufferRelayerAccountData
|
|||
},
|
||||
permission: {
|
||||
publicKey: permission.publicKey,
|
||||
bump: permission.bump,
|
||||
...permission.data.toJSON(),
|
||||
bump: permission.bump,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import { AggregatorAccount } from './aggregatorAccount';
|
|||
import { JobAccount } from './jobAccount';
|
||||
import { QueueAccount } from './queueAccount';
|
||||
|
||||
import * as anchor from '@coral-xyz/anchor';
|
||||
import * as spl from '@solana/spl-token';
|
||||
import {
|
||||
AccountInfo,
|
||||
|
@ -103,7 +102,7 @@ export class LeaseAccount extends Account<types.LeaseAccountData> {
|
|||
queue: PublicKey,
|
||||
aggregator: PublicKey
|
||||
): [LeaseAccount, number] {
|
||||
const [publicKey, bump] = anchor.utils.publicKey.findProgramAddressSync(
|
||||
const [publicKey, bump] = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from('LeaseAccountData'), queue.toBytes(), aggregator.toBytes()],
|
||||
program.programId
|
||||
);
|
||||
|
@ -589,7 +588,7 @@ export class LeaseAccount extends Account<types.LeaseAccountData> {
|
|||
if (!jobAuthority || PublicKey.default.equals(jobAuthority)) {
|
||||
continue;
|
||||
}
|
||||
const [jobWallet, bump] = anchor.utils.publicKey.findProgramAddressSync(
|
||||
const [jobWallet, bump] = PublicKey.findProgramAddressSync(
|
||||
[
|
||||
jobAuthority.toBuffer(),
|
||||
spl.TOKEN_PROGRAM_ID.toBuffer(),
|
||||
|
|
|
@ -164,7 +164,7 @@ export class OracleAccount extends Account<types.OracleAccountData> {
|
|||
queue: PublicKey,
|
||||
wallet: PublicKey
|
||||
): [OracleAccount, number] {
|
||||
const [publicKey, bump] = anchor.utils.publicKey.findProgramAddressSync(
|
||||
const [publicKey, bump] = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from('OracleAccountData'), queue.toBuffer(), wallet.toBuffer()],
|
||||
program.programId
|
||||
);
|
||||
|
|
|
@ -11,7 +11,6 @@ import { TransactionObject } from '../TransactionObject';
|
|||
|
||||
import { Account } from './account';
|
||||
|
||||
import * as anchor from '@coral-xyz/anchor';
|
||||
import { ACCOUNT_DISCRIMINATOR_SIZE } from '@coral-xyz/anchor';
|
||||
import {
|
||||
AccountInfo,
|
||||
|
@ -158,7 +157,7 @@ export class PermissionAccount extends Account<types.PermissionAccountData> {
|
|||
granter: PublicKey,
|
||||
grantee: PublicKey
|
||||
): [PermissionAccount, number] {
|
||||
const [publicKey, bump] = anchor.utils.publicKey.findProgramAddressSync(
|
||||
const [publicKey, bump] = PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from('PermissionAccountData'),
|
||||
authority.toBytes(),
|
||||
|
|
|
@ -239,7 +239,7 @@ export class ProgramStateAccount extends Account<types.SbState> {
|
|||
public static fromSeed(
|
||||
program: SwitchboardProgram
|
||||
): [ProgramStateAccount, number] {
|
||||
const [publicKey, bump] = anchor.utils.publicKey.findProgramAddressSync(
|
||||
const [publicKey, bump] = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from('STATE')],
|
||||
program.programId
|
||||
);
|
||||
|
|
|
@ -20,20 +20,20 @@ export const SWITCHBOARD_LABS_MAINNET_PERMISSIONLESS_CRANK = new PublicKey(
|
|||
|
||||
// devnet permissioned queue
|
||||
export const SWITCHBOARD_LABS_DEVNET_PERMISSIONED_QUEUE = new PublicKey(
|
||||
'GhYg3R1V6DmJbwuc57qZeoYG6gUuvCotUF1zU3WCj98U'
|
||||
'PeRMnAqNqHQYHUuCBEjhm1XPeVTh4BxjY4t4TPan1pG'
|
||||
);
|
||||
// devnet permissioned crank
|
||||
export const SWITCHBOARD_LABS_DEVNET_PERMISSIONED_CRANK = new PublicKey(
|
||||
'85L2cFUvXaeGQ4HrzP8RJEVCL7WvRrXM2msvEmQ82AVr'
|
||||
'crnKsPsuP6f7uiDbAYYw66h2RNBrqoazmtZHwazkC6V'
|
||||
);
|
||||
|
||||
// devnet permissionless queue
|
||||
export const SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE = new PublicKey(
|
||||
'F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy'
|
||||
'uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX'
|
||||
);
|
||||
// devnet permissionless crank
|
||||
export const SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_CRANK = new PublicKey(
|
||||
'GN9jjCy2THzZxhYqZETmPM3my8vg4R5JyNkgULddUMa5'
|
||||
'UcrnK4w2HXCEjY8z6TcQ9tysYr3c9VcFLdYAU9YQP5e'
|
||||
);
|
||||
|
||||
export const DEVNET_GENESIS_HASH =
|
||||
|
|
|
@ -92,3 +92,11 @@ export class AggregatorConfigError extends Error {
|
|||
Object.setPrototypeOf(this, AggregatorConfigError.prototype);
|
||||
}
|
||||
}
|
||||
export class IncorrectOwner extends Error {
|
||||
constructor(expectedOwner: PublicKey, receivedOwner: PublicKey) {
|
||||
super(
|
||||
`incorrect account owner, expected ${expectedOwner}, received ${receivedOwner}`
|
||||
);
|
||||
Object.setPrototypeOf(this, IncorrectOwner.prototype);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ export interface LeaseAccountDataFields {
|
|||
updateCount: BN;
|
||||
/** Public key of keypair that may withdraw funds from the lease at any time */
|
||||
withdrawAuthority: PublicKey;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
bump: number;
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
||||
|
@ -45,6 +47,8 @@ export interface LeaseAccountDataJSON {
|
|||
updateCount: string;
|
||||
/** Public key of keypair that may withdraw funds from the lease at any time */
|
||||
withdrawAuthority: string;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
bump: number;
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
||||
|
@ -68,6 +72,8 @@ export class LeaseAccountData {
|
|||
readonly updateCount: BN;
|
||||
/** Public key of keypair that may withdraw funds from the lease at any time */
|
||||
readonly withdrawAuthority: PublicKey;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
readonly bump: number;
|
||||
readonly ebuf: Array<number>;
|
||||
|
||||
static readonly discriminator = Buffer.from([
|
||||
|
@ -84,7 +90,8 @@ export class LeaseAccountData {
|
|||
borsh.i64('createdAt'),
|
||||
borsh.u128('updateCount'),
|
||||
borsh.publicKey('withdrawAuthority'),
|
||||
borsh.array(borsh.u8(), 256, 'ebuf'),
|
||||
borsh.u8('bump'),
|
||||
borsh.array(borsh.u8(), 255, 'ebuf'),
|
||||
]);
|
||||
|
||||
constructor(fields: LeaseAccountDataFields) {
|
||||
|
@ -97,6 +104,7 @@ export class LeaseAccountData {
|
|||
this.createdAt = fields.createdAt;
|
||||
this.updateCount = fields.updateCount;
|
||||
this.withdrawAuthority = fields.withdrawAuthority;
|
||||
this.bump = fields.bump;
|
||||
this.ebuf = fields.ebuf;
|
||||
}
|
||||
|
||||
|
@ -151,6 +159,7 @@ export class LeaseAccountData {
|
|||
createdAt: dec.createdAt,
|
||||
updateCount: dec.updateCount,
|
||||
withdrawAuthority: dec.withdrawAuthority,
|
||||
bump: dec.bump,
|
||||
ebuf: dec.ebuf,
|
||||
});
|
||||
}
|
||||
|
@ -166,6 +175,7 @@ export class LeaseAccountData {
|
|||
createdAt: this.createdAt.toString(),
|
||||
updateCount: this.updateCount.toString(),
|
||||
withdrawAuthority: this.withdrawAuthority.toString(),
|
||||
bump: this.bump,
|
||||
ebuf: this.ebuf,
|
||||
};
|
||||
}
|
||||
|
@ -181,6 +191,7 @@ export class LeaseAccountData {
|
|||
createdAt: new BN(obj.createdAt),
|
||||
updateCount: new BN(obj.updateCount),
|
||||
withdrawAuthority: new PublicKey(obj.withdrawAuthority),
|
||||
bump: obj.bump,
|
||||
ebuf: obj.ebuf,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ export interface OracleAccountDataFields {
|
|||
queuePubkey: PublicKey;
|
||||
/** Oracle track record. */
|
||||
metrics: types.OracleMetricsFields;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
bump: number;
|
||||
/** Reserved for future info. */
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
@ -42,6 +44,8 @@ export interface OracleAccountDataJSON {
|
|||
queuePubkey: string;
|
||||
/** Oracle track record. */
|
||||
metrics: types.OracleMetricsJSON;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
bump: number;
|
||||
/** Reserved for future info. */
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
@ -63,6 +67,8 @@ export class OracleAccountData {
|
|||
readonly queuePubkey: PublicKey;
|
||||
/** Oracle track record. */
|
||||
readonly metrics: types.OracleMetrics;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
readonly bump: number;
|
||||
/** Reserved for future info. */
|
||||
readonly ebuf: Array<number>;
|
||||
|
||||
|
@ -79,7 +85,8 @@ export class OracleAccountData {
|
|||
borsh.publicKey('tokenAccount'),
|
||||
borsh.publicKey('queuePubkey'),
|
||||
types.OracleMetrics.layout('metrics'),
|
||||
borsh.array(borsh.u8(), 256, 'ebuf'),
|
||||
borsh.u8('bump'),
|
||||
borsh.array(borsh.u8(), 255, 'ebuf'),
|
||||
]);
|
||||
|
||||
constructor(fields: OracleAccountDataFields) {
|
||||
|
@ -91,6 +98,7 @@ export class OracleAccountData {
|
|||
this.tokenAccount = fields.tokenAccount;
|
||||
this.queuePubkey = fields.queuePubkey;
|
||||
this.metrics = new types.OracleMetrics({ ...fields.metrics });
|
||||
this.bump = fields.bump;
|
||||
this.ebuf = fields.ebuf;
|
||||
}
|
||||
|
||||
|
@ -144,6 +152,7 @@ export class OracleAccountData {
|
|||
tokenAccount: dec.tokenAccount,
|
||||
queuePubkey: dec.queuePubkey,
|
||||
metrics: types.OracleMetrics.fromDecoded(dec.metrics),
|
||||
bump: dec.bump,
|
||||
ebuf: dec.ebuf,
|
||||
});
|
||||
}
|
||||
|
@ -158,6 +167,7 @@ export class OracleAccountData {
|
|||
tokenAccount: this.tokenAccount.toString(),
|
||||
queuePubkey: this.queuePubkey.toString(),
|
||||
metrics: this.metrics.toJSON(),
|
||||
bump: this.bump,
|
||||
ebuf: this.ebuf,
|
||||
};
|
||||
}
|
||||
|
@ -172,6 +182,7 @@ export class OracleAccountData {
|
|||
tokenAccount: new PublicKey(obj.tokenAccount),
|
||||
queuePubkey: new PublicKey(obj.queuePubkey),
|
||||
metrics: types.OracleMetrics.fromJSON(obj.metrics),
|
||||
bump: obj.bump,
|
||||
ebuf: obj.ebuf,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ export interface PermissionAccountDataFields {
|
|||
* per account makes sense for the infra. Dont over engineer.
|
||||
*/
|
||||
expiration: BN;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
bump: number;
|
||||
/** Reserved for future info. */
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
@ -38,6 +40,8 @@ export interface PermissionAccountDataJSON {
|
|||
* per account makes sense for the infra. Dont over engineer.
|
||||
*/
|
||||
expiration: string;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
bump: number;
|
||||
/** Reserved for future info. */
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
@ -57,6 +61,8 @@ export class PermissionAccountData {
|
|||
* per account makes sense for the infra. Dont over engineer.
|
||||
*/
|
||||
readonly expiration: BN;
|
||||
/** The PDA bump to derive the pubkey. */
|
||||
readonly bump: number;
|
||||
/** Reserved for future info. */
|
||||
readonly ebuf: Array<number>;
|
||||
|
||||
|
@ -70,7 +76,8 @@ export class PermissionAccountData {
|
|||
borsh.publicKey('granter'),
|
||||
borsh.publicKey('grantee'),
|
||||
borsh.i64('expiration'),
|
||||
borsh.array(borsh.u8(), 256, 'ebuf'),
|
||||
borsh.u8('bump'),
|
||||
borsh.array(borsh.u8(), 255, 'ebuf'),
|
||||
]);
|
||||
|
||||
constructor(fields: PermissionAccountDataFields) {
|
||||
|
@ -79,6 +86,7 @@ export class PermissionAccountData {
|
|||
this.granter = fields.granter;
|
||||
this.grantee = fields.grantee;
|
||||
this.expiration = fields.expiration;
|
||||
this.bump = fields.bump;
|
||||
this.ebuf = fields.ebuf;
|
||||
}
|
||||
|
||||
|
@ -129,6 +137,7 @@ export class PermissionAccountData {
|
|||
granter: dec.granter,
|
||||
grantee: dec.grantee,
|
||||
expiration: dec.expiration,
|
||||
bump: dec.bump,
|
||||
ebuf: dec.ebuf,
|
||||
});
|
||||
}
|
||||
|
@ -140,6 +149,7 @@ export class PermissionAccountData {
|
|||
granter: this.granter.toString(),
|
||||
grantee: this.grantee.toString(),
|
||||
expiration: this.expiration.toString(),
|
||||
bump: this.bump,
|
||||
ebuf: this.ebuf,
|
||||
};
|
||||
}
|
||||
|
@ -151,6 +161,7 @@ export class PermissionAccountData {
|
|||
granter: new PublicKey(obj.granter),
|
||||
grantee: new PublicKey(obj.grantee),
|
||||
expiration: new BN(obj.expiration),
|
||||
bump: obj.bump,
|
||||
ebuf: obj.ebuf,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,11 @@ export interface SbStateFields {
|
|||
tokenVault: PublicKey;
|
||||
/** The token mint used by the DAO. */
|
||||
daoMint: PublicKey;
|
||||
/** Reserved for future info. */
|
||||
/**
|
||||
* Reserved for future info.
|
||||
* The PDA bump to derive the pubkey.
|
||||
*/
|
||||
bump: number;
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
||||
|
@ -26,7 +30,11 @@ export interface SbStateJSON {
|
|||
tokenVault: string;
|
||||
/** The token mint used by the DAO. */
|
||||
daoMint: string;
|
||||
/** Reserved for future info. */
|
||||
/**
|
||||
* Reserved for future info.
|
||||
* The PDA bump to derive the pubkey.
|
||||
*/
|
||||
bump: number;
|
||||
ebuf: Array<number>;
|
||||
}
|
||||
|
||||
|
@ -39,7 +47,11 @@ export class SbState {
|
|||
readonly tokenVault: PublicKey;
|
||||
/** The token mint used by the DAO. */
|
||||
readonly daoMint: PublicKey;
|
||||
/** Reserved for future info. */
|
||||
/**
|
||||
* Reserved for future info.
|
||||
* The PDA bump to derive the pubkey.
|
||||
*/
|
||||
readonly bump: number;
|
||||
readonly ebuf: Array<number>;
|
||||
|
||||
static readonly discriminator = Buffer.from([
|
||||
|
@ -51,7 +63,8 @@ export class SbState {
|
|||
borsh.publicKey('tokenMint'),
|
||||
borsh.publicKey('tokenVault'),
|
||||
borsh.publicKey('daoMint'),
|
||||
borsh.array(borsh.u8(), 992, 'ebuf'),
|
||||
borsh.u8('bump'),
|
||||
borsh.array(borsh.u8(), 991, 'ebuf'),
|
||||
]);
|
||||
|
||||
constructor(fields: SbStateFields) {
|
||||
|
@ -59,6 +72,7 @@ export class SbState {
|
|||
this.tokenMint = fields.tokenMint;
|
||||
this.tokenVault = fields.tokenVault;
|
||||
this.daoMint = fields.daoMint;
|
||||
this.bump = fields.bump;
|
||||
this.ebuf = fields.ebuf;
|
||||
}
|
||||
|
||||
|
@ -108,6 +122,7 @@ export class SbState {
|
|||
tokenMint: dec.tokenMint,
|
||||
tokenVault: dec.tokenVault,
|
||||
daoMint: dec.daoMint,
|
||||
bump: dec.bump,
|
||||
ebuf: dec.ebuf,
|
||||
});
|
||||
}
|
||||
|
@ -118,6 +133,7 @@ export class SbState {
|
|||
tokenMint: this.tokenMint.toString(),
|
||||
tokenVault: this.tokenVault.toString(),
|
||||
daoMint: this.daoMint.toString(),
|
||||
bump: this.bump,
|
||||
ebuf: this.ebuf,
|
||||
};
|
||||
}
|
||||
|
@ -128,6 +144,7 @@ export class SbState {
|
|||
tokenMint: new PublicKey(obj.tokenMint),
|
||||
tokenVault: new PublicKey(obj.tokenVault),
|
||||
daoMint: new PublicKey(obj.daoMint),
|
||||
bump: obj.bump,
|
||||
ebuf: obj.ebuf,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import { SwitchboardProgram } from '../../SwitchboardProgram';
|
||||
import {
|
||||
TransactionInstruction,
|
||||
PublicKey,
|
||||
AccountMeta,
|
||||
} from '@solana/web3.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { BN } from '@switchboard-xyz/common'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as borsh from '@coral-xyz/borsh'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as types from '../types'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
|
||||
export interface AggregatorCloseArgs {
|
||||
params: types.AggregatorCloseParamsFields;
|
||||
}
|
||||
|
||||
export interface AggregatorCloseAccounts {
|
||||
authority: PublicKey;
|
||||
aggregator: PublicKey;
|
||||
permission: PublicKey;
|
||||
lease: PublicKey;
|
||||
escrow: PublicKey;
|
||||
oracleQueue: PublicKey;
|
||||
queueAuthority: PublicKey;
|
||||
programState: PublicKey;
|
||||
solDest: PublicKey;
|
||||
escrowDest: PublicKey;
|
||||
tokenProgram: PublicKey;
|
||||
/** Optional accounts */
|
||||
crank: PublicKey;
|
||||
dataBuffer: PublicKey;
|
||||
}
|
||||
|
||||
export const layout = borsh.struct([
|
||||
types.AggregatorCloseParams.layout('params'),
|
||||
]);
|
||||
|
||||
export function aggregatorClose(
|
||||
program: SwitchboardProgram,
|
||||
args: AggregatorCloseArgs,
|
||||
accounts: AggregatorCloseAccounts
|
||||
) {
|
||||
const keys: Array<AccountMeta> = [
|
||||
{ pubkey: accounts.authority, isSigner: true, isWritable: false },
|
||||
{ pubkey: accounts.aggregator, isSigner: false, isWritable: true },
|
||||
{ pubkey: accounts.permission, isSigner: false, isWritable: true },
|
||||
{ pubkey: accounts.lease, isSigner: false, isWritable: true },
|
||||
{ pubkey: accounts.escrow, isSigner: false, isWritable: true },
|
||||
{ pubkey: accounts.oracleQueue, isSigner: false, isWritable: false },
|
||||
{ pubkey: accounts.queueAuthority, isSigner: false, isWritable: false },
|
||||
{ pubkey: accounts.programState, isSigner: false, isWritable: false },
|
||||
{ pubkey: accounts.solDest, isSigner: false, isWritable: false },
|
||||
{ pubkey: accounts.escrowDest, isSigner: false, isWritable: true },
|
||||
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
||||
{ pubkey: accounts.crank, isSigner: false, isWritable: true },
|
||||
{ pubkey: accounts.dataBuffer, isSigner: false, isWritable: true },
|
||||
];
|
||||
const identifier = Buffer.from([77, 29, 85, 88, 224, 181, 157, 69]);
|
||||
const buffer = Buffer.alloc(1000);
|
||||
const len = layout.encode(
|
||||
{
|
||||
params: types.AggregatorCloseParams.toEncodable(args.params),
|
||||
},
|
||||
buffer
|
||||
);
|
||||
const data = Buffer.concat([identifier, buffer]).slice(0, 8 + len);
|
||||
const ix = new TransactionInstruction({
|
||||
keys,
|
||||
programId: program.programId,
|
||||
data,
|
||||
});
|
||||
return ix;
|
||||
}
|
|
@ -1,3 +1,10 @@
|
|||
export { aggregatorClose } from './aggregatorClose';
|
||||
export type {
|
||||
AggregatorCloseArgs,
|
||||
AggregatorCloseAccounts,
|
||||
} from './aggregatorClose';
|
||||
export { setBumps } from './setBumps';
|
||||
export type { SetBumpsArgs, SetBumpsAccounts } from './setBumps';
|
||||
export { aggregatorAddJob } from './aggregatorAddJob';
|
||||
export type {
|
||||
AggregatorAddJobArgs,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { SwitchboardProgram } from '../../SwitchboardProgram';
|
||||
import {
|
||||
TransactionInstruction,
|
||||
PublicKey,
|
||||
AccountMeta,
|
||||
} from '@solana/web3.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { BN } from '@switchboard-xyz/common'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as borsh from '@coral-xyz/borsh'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as types from '../types'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
|
||||
export interface SetBumpsArgs {
|
||||
params: types.SetBumpsParamsFields;
|
||||
}
|
||||
|
||||
export interface SetBumpsAccounts {
|
||||
state: PublicKey;
|
||||
}
|
||||
|
||||
export const layout = borsh.struct([types.SetBumpsParams.layout('params')]);
|
||||
|
||||
export function setBumps(
|
||||
program: SwitchboardProgram,
|
||||
args: SetBumpsArgs,
|
||||
accounts: SetBumpsAccounts
|
||||
) {
|
||||
const keys: Array<AccountMeta> = [
|
||||
{ pubkey: accounts.state, isSigner: false, isWritable: true },
|
||||
];
|
||||
const identifier = Buffer.from([19, 216, 193, 244, 22, 47, 180, 64]);
|
||||
const buffer = Buffer.alloc(1000);
|
||||
const len = layout.encode(
|
||||
{
|
||||
params: types.SetBumpsParams.toEncodable(args.params),
|
||||
},
|
||||
buffer
|
||||
);
|
||||
const data = Buffer.concat([identifier, buffer]).slice(0, 8 + len);
|
||||
const ix = new TransactionInstruction({
|
||||
keys,
|
||||
programId: program.programId,
|
||||
data,
|
||||
});
|
||||
return ix;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
import { SwitchboardProgram } from '../../SwitchboardProgram';
|
||||
import { PublicKey } from '@solana/web3.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { BN } from '@switchboard-xyz/common'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as types from '../types'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as borsh from '@coral-xyz/borsh';
|
||||
|
||||
export interface AggregatorCloseParamsFields {
|
||||
stateBump: number;
|
||||
permissionBump: number;
|
||||
leaseBump: number;
|
||||
}
|
||||
|
||||
export interface AggregatorCloseParamsJSON {
|
||||
stateBump: number;
|
||||
permissionBump: number;
|
||||
leaseBump: number;
|
||||
}
|
||||
|
||||
export class AggregatorCloseParams {
|
||||
readonly stateBump: number;
|
||||
readonly permissionBump: number;
|
||||
readonly leaseBump: number;
|
||||
|
||||
constructor(fields: AggregatorCloseParamsFields) {
|
||||
this.stateBump = fields.stateBump;
|
||||
this.permissionBump = fields.permissionBump;
|
||||
this.leaseBump = fields.leaseBump;
|
||||
}
|
||||
|
||||
static layout(property?: string) {
|
||||
return borsh.struct(
|
||||
[
|
||||
borsh.u8('stateBump'),
|
||||
borsh.u8('permissionBump'),
|
||||
borsh.u8('leaseBump'),
|
||||
],
|
||||
property
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static fromDecoded(obj: any) {
|
||||
return new AggregatorCloseParams({
|
||||
stateBump: obj.stateBump,
|
||||
permissionBump: obj.permissionBump,
|
||||
leaseBump: obj.leaseBump,
|
||||
});
|
||||
}
|
||||
|
||||
static toEncodable(fields: AggregatorCloseParamsFields) {
|
||||
return {
|
||||
stateBump: fields.stateBump,
|
||||
permissionBump: fields.permissionBump,
|
||||
leaseBump: fields.leaseBump,
|
||||
};
|
||||
}
|
||||
|
||||
toJSON(): AggregatorCloseParamsJSON {
|
||||
return {
|
||||
stateBump: this.stateBump,
|
||||
permissionBump: this.permissionBump,
|
||||
leaseBump: this.leaseBump,
|
||||
};
|
||||
}
|
||||
|
||||
static fromJSON(obj: AggregatorCloseParamsJSON): AggregatorCloseParams {
|
||||
return new AggregatorCloseParams({
|
||||
stateBump: obj.stateBump,
|
||||
permissionBump: obj.permissionBump,
|
||||
leaseBump: obj.leaseBump,
|
||||
});
|
||||
}
|
||||
|
||||
toEncodable() {
|
||||
return AggregatorCloseParams.toEncodable(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { SwitchboardProgram } from '../../SwitchboardProgram';
|
||||
import { PublicKey } from '@solana/web3.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { BN } from '@switchboard-xyz/common'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as types from '../types'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import * as borsh from '@coral-xyz/borsh';
|
||||
|
||||
export interface SetBumpsParamsFields {
|
||||
stateBump: number;
|
||||
}
|
||||
|
||||
export interface SetBumpsParamsJSON {
|
||||
stateBump: number;
|
||||
}
|
||||
|
||||
export class SetBumpsParams {
|
||||
readonly stateBump: number;
|
||||
|
||||
constructor(fields: SetBumpsParamsFields) {
|
||||
this.stateBump = fields.stateBump;
|
||||
}
|
||||
|
||||
static layout(property?: string) {
|
||||
return borsh.struct([borsh.u8('stateBump')], property);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static fromDecoded(obj: any) {
|
||||
return new SetBumpsParams({
|
||||
stateBump: obj.stateBump,
|
||||
});
|
||||
}
|
||||
|
||||
static toEncodable(fields: SetBumpsParamsFields) {
|
||||
return {
|
||||
stateBump: fields.stateBump,
|
||||
};
|
||||
}
|
||||
|
||||
toJSON(): SetBumpsParamsJSON {
|
||||
return {
|
||||
stateBump: this.stateBump,
|
||||
};
|
||||
}
|
||||
|
||||
static fromJSON(obj: SetBumpsParamsJSON): SetBumpsParams {
|
||||
return new SetBumpsParams({
|
||||
stateBump: obj.stateBump,
|
||||
});
|
||||
}
|
||||
|
||||
toEncodable() {
|
||||
return SetBumpsParams.toEncodable(this);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,11 @@ export type {
|
|||
AggregatorAddJobParamsFields,
|
||||
AggregatorAddJobParamsJSON,
|
||||
} from './AggregatorAddJobParams';
|
||||
export { AggregatorCloseParams } from './AggregatorCloseParams';
|
||||
export type {
|
||||
AggregatorCloseParamsFields,
|
||||
AggregatorCloseParamsJSON,
|
||||
} from './AggregatorCloseParams';
|
||||
export { AggregatorHistoryRow } from './AggregatorHistoryRow';
|
||||
export type {
|
||||
AggregatorHistoryRowFields,
|
||||
|
@ -271,6 +276,11 @@ export type {
|
|||
} from './ProjectivePointZC';
|
||||
export { Scalar } from './Scalar';
|
||||
export type { ScalarFields, ScalarJSON } from './Scalar';
|
||||
export { SetBumpsParams } from './SetBumpsParams';
|
||||
export type {
|
||||
SetBumpsParamsFields,
|
||||
SetBumpsParamsJSON,
|
||||
} from './SetBumpsParams';
|
||||
export { SlidingWindowElement } from './SlidingWindowElement';
|
||||
export type {
|
||||
SlidingWindowElementFields,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -117,7 +117,7 @@ export class Mint {
|
|||
owner: PublicKey,
|
||||
mint: PublicKey
|
||||
): PublicKey {
|
||||
const [associatedToken] = anchor.utils.publicKey.findProgramAddressSync(
|
||||
const [associatedToken] = PublicKey.findProgramAddressSync(
|
||||
[owner.toBuffer(), spl.TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
|
|
@ -23,7 +23,7 @@ export const LATEST_DOCKER_VERSION = 'dev-v2-RC_01_05_23_03_24';
|
|||
* @return the publicKey of the address holding the upgradeable program buffer
|
||||
*/
|
||||
export const getProgramDataAddress = (programId: PublicKey): PublicKey => {
|
||||
return anchor.utils.publicKey.findProgramAddressSync(
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[programId.toBytes()],
|
||||
new PublicKey('BPFLoaderUpgradeab1e11111111111111111111111')
|
||||
)[0];
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
import 'mocha';
|
||||
|
||||
import * as sbv2 from '../src';
|
||||
import { CrankAccount, QueueAccount } from '../src';
|
||||
|
||||
import { setupTest, TestContext } from './utils';
|
||||
|
||||
import { Keypair, PublicKey } from '@solana/web3.js';
|
||||
import { OracleJob, sleep } from '@switchboard-xyz/common';
|
||||
import assert from 'assert';
|
||||
|
||||
describe('Close Aggregator Tests', () => {
|
||||
let ctx: TestContext;
|
||||
|
||||
const queueAuthority = Keypair.generate();
|
||||
let queueAccount: QueueAccount;
|
||||
|
||||
let payerTokenWallet: PublicKey;
|
||||
let crankAccount: CrankAccount;
|
||||
|
||||
before(async () => {
|
||||
if (process.env.SOLANA_LOCALNET) {
|
||||
return;
|
||||
}
|
||||
ctx = await setupTest();
|
||||
|
||||
[queueAccount] = await sbv2.QueueAccount.create(ctx.program, {
|
||||
name: 'aggregator-queue',
|
||||
metadata: '',
|
||||
authority: queueAuthority.publicKey,
|
||||
queueSize: 1,
|
||||
reward: 0,
|
||||
minStake: 0,
|
||||
oracleTimeout: 86400,
|
||||
slashingEnabled: false,
|
||||
unpermissionedFeeds: true,
|
||||
unpermissionedVrf: true,
|
||||
enableBufferRelayers: false,
|
||||
});
|
||||
|
||||
[crankAccount] = await queueAccount.createCrank({
|
||||
maxRows: 10,
|
||||
name: 'Crank-1',
|
||||
});
|
||||
|
||||
payerTokenWallet = await ctx.program.mint.getOrCreateAssociatedUser(
|
||||
ctx.program.walletPubkey
|
||||
);
|
||||
|
||||
// add a single oracle for open round calls
|
||||
const [oracleAccount] = await queueAccount.createOracle({
|
||||
name: 'oracle-1',
|
||||
enable: true,
|
||||
queueAuthority: queueAuthority,
|
||||
});
|
||||
await oracleAccount.heartbeat();
|
||||
});
|
||||
|
||||
it('Creates and closes an aggregator not on a crank', async () => {
|
||||
const [aggregatorAccount] = await queueAccount.createFeed({
|
||||
queueAuthority: queueAuthority,
|
||||
batchSize: 1,
|
||||
minRequiredOracleResults: 1,
|
||||
minRequiredJobResults: 1,
|
||||
minUpdateDelaySeconds: 60,
|
||||
enable: true,
|
||||
historyLimit: 1000,
|
||||
jobs: [
|
||||
{
|
||||
data: OracleJob.encodeDelimited(
|
||||
OracleJob.fromObject({
|
||||
tasks: [
|
||||
{
|
||||
valueTask: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).finish(),
|
||||
name: 'Job1',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(`Created aggregator ${aggregatorAccount.publicKey}`);
|
||||
|
||||
const {
|
||||
permissionAccount,
|
||||
permissionBump,
|
||||
leaseAccount,
|
||||
leaseBump,
|
||||
leaseEscrow,
|
||||
} = aggregatorAccount.getAccounts(queueAccount, queueAuthority.publicKey);
|
||||
|
||||
await sleep(1000);
|
||||
const initialAggregatorState = await aggregatorAccount.loadData();
|
||||
|
||||
console.log(`loaded aggregator, ${aggregatorAccount.publicKey}`);
|
||||
|
||||
const closeTxn = await aggregatorAccount.closeInstructions(
|
||||
ctx.program.walletPubkey
|
||||
);
|
||||
const closeSig = await ctx.program.signAndSend(closeTxn, {
|
||||
skipPreflight: true,
|
||||
});
|
||||
console.log(closeSig);
|
||||
|
||||
const parsedTxnLogs =
|
||||
await queueAccount.program.connection.getParsedTransaction(closeSig);
|
||||
console.log(JSON.stringify(parsedTxnLogs?.meta?.logMessages, undefined, 2));
|
||||
|
||||
const finalAggregatorState =
|
||||
await queueAccount.program.connection.getAccountInfo(
|
||||
aggregatorAccount.publicKey
|
||||
);
|
||||
assert(finalAggregatorState === null, 'AggregatorAccount was not closed');
|
||||
|
||||
const finalPermissionState =
|
||||
await queueAccount.program.connection.getAccountInfo(
|
||||
permissionAccount.publicKey
|
||||
);
|
||||
assert(finalPermissionState === null, 'PermissionAccount was not closed');
|
||||
|
||||
const finalLeaseState =
|
||||
await queueAccount.program.connection.getAccountInfo(
|
||||
leaseAccount.publicKey
|
||||
);
|
||||
assert(finalLeaseState === null, 'LeaseAccount was not closed');
|
||||
});
|
||||
|
||||
it('Creates and closes an aggregator with a crank', async () => {
|
||||
const [aggregatorAccount] = await queueAccount.createFeed({
|
||||
queueAuthority: queueAuthority,
|
||||
batchSize: 1,
|
||||
minRequiredOracleResults: 1,
|
||||
minRequiredJobResults: 1,
|
||||
minUpdateDelaySeconds: 60,
|
||||
enable: true,
|
||||
crankPubkey: crankAccount.publicKey,
|
||||
historyLimit: 1000,
|
||||
jobs: [
|
||||
{
|
||||
data: OracleJob.encodeDelimited(
|
||||
OracleJob.fromObject({
|
||||
tasks: [
|
||||
{
|
||||
valueTask: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).finish(),
|
||||
name: 'Job1',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(`Created aggregator ${aggregatorAccount.publicKey}`);
|
||||
|
||||
const {
|
||||
permissionAccount,
|
||||
permissionBump,
|
||||
leaseAccount,
|
||||
leaseBump,
|
||||
leaseEscrow,
|
||||
} = aggregatorAccount.getAccounts(queueAccount, queueAuthority.publicKey);
|
||||
|
||||
console.log(`permission: ${permissionAccount.publicKey}`);
|
||||
|
||||
const initialAggregatorState = await aggregatorAccount.loadData();
|
||||
|
||||
const initialCrankRows = await crankAccount.loadCrank();
|
||||
const crankIdx = initialCrankRows.findIndex(r =>
|
||||
r.pubkey.equals(aggregatorAccount.publicKey)
|
||||
);
|
||||
assert(crankIdx !== -1, 'Aggregator initially missing from the crank');
|
||||
|
||||
const closeTxn = await aggregatorAccount.closeInstructions(
|
||||
ctx.program.walletPubkey
|
||||
);
|
||||
const closeSig = await ctx.program.signAndSend(closeTxn, {
|
||||
skipPreflight: true,
|
||||
});
|
||||
console.log(closeSig);
|
||||
|
||||
const parsedTxnLogs =
|
||||
await queueAccount.program.connection.getParsedTransaction(closeSig);
|
||||
console.log(JSON.stringify(parsedTxnLogs?.meta?.logMessages, undefined, 2));
|
||||
|
||||
const finalAggregatorState =
|
||||
await queueAccount.program.connection.getAccountInfo(
|
||||
aggregatorAccount.publicKey
|
||||
);
|
||||
assert(finalAggregatorState === null, 'AggregatorAccount was not closed');
|
||||
|
||||
const finalPermissionState =
|
||||
await queueAccount.program.connection.getAccountInfo(
|
||||
permissionAccount.publicKey
|
||||
);
|
||||
assert(finalPermissionState === null, 'PermissionAccount was not closed');
|
||||
|
||||
const finalLeaseState =
|
||||
await queueAccount.program.connection.getAccountInfo(
|
||||
leaseAccount.publicKey
|
||||
);
|
||||
assert(finalLeaseState === null, 'LeaseAccount was not closed');
|
||||
|
||||
const finalCrankRows = await crankAccount.loadCrank();
|
||||
const newCrankIdx = finalCrankRows.findIndex(r =>
|
||||
r.pubkey.equals(aggregatorAccount.publicKey)
|
||||
);
|
||||
assert(newCrankIdx === -1, 'Aggregator is still on the crank');
|
||||
});
|
||||
});
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,219 @@
|
|||
import * as sbv2 from './src';
|
||||
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { Big, BN, IOracleJob, toUtf8 } from '@switchboard-xyz/common';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const ignoreFields = [
|
||||
'program',
|
||||
'jobHashes',
|
||||
'jobsChecksum',
|
||||
'currentRound',
|
||||
'latestConfirmedRound',
|
||||
];
|
||||
|
||||
export function setupOutputDir(programId: string) {
|
||||
const dirPath = path.join(__dirname, programId);
|
||||
const feedDirPath = path.join(dirPath, 'feeds');
|
||||
if (!fs.existsSync(feedDirPath)) {
|
||||
fs.mkdirSync(feedDirPath, { recursive: true });
|
||||
}
|
||||
const jobDirPath = path.join(dirPath, 'jobs');
|
||||
if (!fs.existsSync(jobDirPath)) {
|
||||
fs.mkdirSync(jobDirPath, { recursive: true });
|
||||
}
|
||||
// const jobCsv = path.join(__dirname, 'jobs.csv');
|
||||
// if (!fs.existsSync(jobCsv)) {
|
||||
// fs.writeFileSync(jobCsv, `oldPubkey, newPubkey\n`);
|
||||
// }
|
||||
// const aggregatorCsv = path.join(__dirname, 'aggregator.csv');
|
||||
// if (!fs.existsSync(aggregatorCsv)) {
|
||||
// fs.writeFileSync(aggregatorCsv, `oldPubkey, newPubkey\n`);
|
||||
// }
|
||||
|
||||
return [dirPath, feedDirPath, jobDirPath];
|
||||
}
|
||||
|
||||
export function jsonReplacers(key, value) {
|
||||
if (ignoreFields.includes(key)) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
!value ||
|
||||
typeof value === 'string' ||
|
||||
typeof value === 'number' ||
|
||||
typeof value === 'boolean'
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (
|
||||
key === 'ebuf' ||
|
||||
key === '_ebuf' ||
|
||||
key === 'reserved' ||
|
||||
key === 'reserved1'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof PublicKey) {
|
||||
return value.toBase58();
|
||||
}
|
||||
|
||||
if ((key === 'name' || key === 'metadata') && Array.isArray(value)) {
|
||||
return toUtf8(value);
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
if (typeof value[0] === 'number') {
|
||||
if (value.every(item => item === 0)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return `[${value.join(',')}]`;
|
||||
}
|
||||
|
||||
if (value[0] instanceof PublicKey) {
|
||||
return value.filter(
|
||||
pubkey => !(pubkey as PublicKey).equals(PublicKey.default)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
value instanceof sbv2.types.SwitchboardDecimal ||
|
||||
('mantissa' in value && 'scale' in value)
|
||||
) {
|
||||
const big = sbv2.types.SwitchboardDecimal.from(value).toBig();
|
||||
return big.toString();
|
||||
}
|
||||
|
||||
if (value instanceof Big) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (BN.isBN(value)) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export interface IAggregatorDefinition {
|
||||
account: {
|
||||
publicKey: string;
|
||||
size: number;
|
||||
};
|
||||
data: {
|
||||
name: string;
|
||||
metadata: string;
|
||||
queuePubkey: string;
|
||||
oracleRequestBatchSize: number;
|
||||
minOracleResults: number;
|
||||
minJobResults: number;
|
||||
minUpdateDelaySeconds: number;
|
||||
startAfter: string;
|
||||
varianceThreshold: string;
|
||||
forceReportPeriod: string;
|
||||
expiration: string;
|
||||
consecutiveFailureCount: string;
|
||||
nextAllowedUpdateTime: string;
|
||||
isLocked: boolean;
|
||||
crankPubkey: string;
|
||||
jobPubkeysData: Array<string>;
|
||||
authority: string;
|
||||
historyBuffer: string;
|
||||
resolutionMode: {
|
||||
kind: string;
|
||||
};
|
||||
basePriorityFee: number;
|
||||
priorityFeeBump: number;
|
||||
priorityFeeBumpPeriod: number;
|
||||
maxPriorityFeeMultiplier: number;
|
||||
};
|
||||
permissionAccount: {
|
||||
publicKey: string;
|
||||
size: number;
|
||||
};
|
||||
permissions: {
|
||||
authority: string;
|
||||
permissions: number;
|
||||
granter: string;
|
||||
grantee: string;
|
||||
expiration: string;
|
||||
};
|
||||
leaseAccount: {
|
||||
publicKey: string;
|
||||
size: number;
|
||||
};
|
||||
lease: {
|
||||
escrow: string;
|
||||
queue: string;
|
||||
aggregator: string;
|
||||
tokenProgram: string;
|
||||
isActive: boolean;
|
||||
crankRowCount: number;
|
||||
createdAt: string;
|
||||
withdrawAuthority: string;
|
||||
};
|
||||
balance: number;
|
||||
jobs: Array<IJobDefinition>;
|
||||
}
|
||||
|
||||
export interface Aggregator {
|
||||
publicKey: string;
|
||||
definition: {
|
||||
name: string;
|
||||
metadata: string;
|
||||
batchSize: number;
|
||||
minRequiredOracleResults: number;
|
||||
minRequiredJobResults: number;
|
||||
minUpdateDelaySeconds: number;
|
||||
historyBufferLength?: number;
|
||||
startAfter: number;
|
||||
varianceThreshold: number;
|
||||
forceReportPeriod: number;
|
||||
expiration: number;
|
||||
pushCrank: boolean;
|
||||
disableCrank: boolean;
|
||||
authority: string;
|
||||
slidingWindow: boolean;
|
||||
basePriorityFee: number;
|
||||
priorityFeeBump: number;
|
||||
priorityFeeBumpPeriod: number;
|
||||
maxPriorityFeeMultiplier: number;
|
||||
jobs: Array<{ pubkey: string; weight: number }>;
|
||||
};
|
||||
data: IAggregatorDefinition;
|
||||
}
|
||||
|
||||
export interface IJobDefinition {
|
||||
account: {
|
||||
publicKey: string;
|
||||
};
|
||||
data: {
|
||||
name: string;
|
||||
metadata: string;
|
||||
authority: string;
|
||||
expiration: string;
|
||||
hash: string;
|
||||
data: string;
|
||||
referenceCount: number;
|
||||
totalSpent: string;
|
||||
createdAt: string;
|
||||
isInitializing: number;
|
||||
};
|
||||
oracleJob: IOracleJob;
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
publicKey: string;
|
||||
definition: {
|
||||
data: string;
|
||||
name: string;
|
||||
authority: string;
|
||||
expiration: number;
|
||||
};
|
||||
data: IJobDefinition;
|
||||
}
|
|
@ -53,8 +53,8 @@ importers:
|
|||
|
||||
javascript/solana.js:
|
||||
specifiers:
|
||||
'@coral-xyz/anchor': ^0.26.0
|
||||
'@coral-xyz/borsh': ^0.26.0
|
||||
'@coral-xyz/anchor': ^0.27.0
|
||||
'@coral-xyz/borsh': ^0.27.0
|
||||
'@solana/spl-token': ^0.3.6
|
||||
'@solana/web3.js': ^1.73.0
|
||||
'@switchboard-xyz/common': ^2.1.33
|
||||
|
@ -69,6 +69,7 @@ importers:
|
|||
chalk: ^4.1.2
|
||||
dotenv: ^16.0.3
|
||||
eslint: ^8.35.0
|
||||
exponential-backoff: ^3.1.1
|
||||
gts: ^3.1.1
|
||||
lodash: ^4.17.21
|
||||
mocha: ^10.1.0
|
||||
|
@ -79,8 +80,8 @@ importers:
|
|||
typedoc: ^0.23.23
|
||||
typescript: ^4.9.4
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.26.0
|
||||
'@coral-xyz/borsh': 0.26.0_@solana+web3.js@1.73.0
|
||||
'@coral-xyz/anchor': 0.27.0
|
||||
'@coral-xyz/borsh': 0.27.0_@solana+web3.js@1.73.0
|
||||
'@solana/spl-token': 0.3.6_@solana+web3.js@1.73.0
|
||||
'@solana/web3.js': 1.73.0
|
||||
'@switchboard-xyz/common': 2.1.33
|
||||
|
@ -97,6 +98,7 @@ importers:
|
|||
chai: 4.3.7
|
||||
chalk: 4.1.2
|
||||
eslint: 8.35.0
|
||||
exponential-backoff: 3.1.1
|
||||
gts: 3.1.1_typescript@4.9.4
|
||||
mocha: 10.1.0
|
||||
shelljs: 0.8.5
|
||||
|
@ -108,7 +110,7 @@ importers:
|
|||
|
||||
programs/anchor-buffer-parser:
|
||||
specifiers:
|
||||
'@coral-xyz/anchor': ^0.26.0
|
||||
'@coral-xyz/anchor': ^0.27.0
|
||||
'@solana/web3.js': ^1.73.3
|
||||
'@switchboard-xyz/common': ^2.1.33
|
||||
'@switchboard-xyz/solana.js': workspace:*
|
||||
|
@ -123,7 +125,7 @@ importers:
|
|||
ts-node: ^10.4.0
|
||||
typescript: ^4.7
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.26.0
|
||||
'@coral-xyz/anchor': 0.27.0
|
||||
'@solana/web3.js': 1.73.3
|
||||
'@switchboard-xyz/common': 2.1.33
|
||||
'@switchboard-xyz/solana.js': link:../../javascript/solana.js
|
||||
|
@ -141,7 +143,7 @@ importers:
|
|||
|
||||
programs/anchor-feed-parser:
|
||||
specifiers:
|
||||
'@coral-xyz/anchor': ^0.26.0
|
||||
'@coral-xyz/anchor': ^0.27.0
|
||||
'@solana/web3.js': ^1.73.3
|
||||
'@switchboard-xyz/common': ^2.1.33
|
||||
'@switchboard-xyz/solana.js': workspace:*
|
||||
|
@ -154,7 +156,7 @@ importers:
|
|||
ts-node: ^10.4.0
|
||||
typescript: ^4.7
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.26.0
|
||||
'@coral-xyz/anchor': 0.27.0
|
||||
'@solana/web3.js': 1.73.3
|
||||
'@switchboard-xyz/common': 2.1.33
|
||||
'@switchboard-xyz/solana.js': link:../../javascript/solana.js
|
||||
|
@ -170,7 +172,7 @@ importers:
|
|||
|
||||
programs/anchor-history-parser:
|
||||
specifiers:
|
||||
'@coral-xyz/anchor': ^0.26.0
|
||||
'@coral-xyz/anchor': ^0.27.0
|
||||
'@switchboard-xyz/common': ^2.1.33
|
||||
'@switchboard-xyz/solana.js': workspace:*
|
||||
'@types/bn.js': ^5.1.0
|
||||
|
@ -182,7 +184,7 @@ importers:
|
|||
ts-mocha: ^10.0.0
|
||||
typescript: ^4.3.5
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.26.0
|
||||
'@coral-xyz/anchor': 0.27.0
|
||||
'@switchboard-xyz/common': 2.1.33
|
||||
'@switchboard-xyz/solana.js': link:../../javascript/solana.js
|
||||
devDependencies:
|
||||
|
@ -197,8 +199,8 @@ importers:
|
|||
|
||||
programs/anchor-vrf-parser:
|
||||
specifiers:
|
||||
'@coral-xyz/anchor': ^0.26.0
|
||||
'@coral-xyz/borsh': ^0.26.0
|
||||
'@coral-xyz/anchor': ^0.27.0
|
||||
'@coral-xyz/borsh': ^0.27.0
|
||||
'@project-serum/borsh': ^0.2.5
|
||||
'@solana/spl-token': ^0.3.6
|
||||
'@solana/web3.js': ^1.73.3
|
||||
|
@ -223,8 +225,8 @@ importers:
|
|||
typescript: ^4.9.3
|
||||
yargs: ^17.5.1
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.26.0
|
||||
'@coral-xyz/borsh': 0.26.0_@solana+web3.js@1.73.3
|
||||
'@coral-xyz/anchor': 0.27.0
|
||||
'@coral-xyz/borsh': 0.27.0_@solana+web3.js@1.73.3
|
||||
'@project-serum/borsh': 0.2.5_@solana+web3.js@1.73.3
|
||||
'@solana/spl-token': 0.3.6_@solana+web3.js@1.73.3
|
||||
'@solana/web3.js': 1.73.3
|
||||
|
@ -252,7 +254,7 @@ importers:
|
|||
|
||||
programs/native-feed-parser:
|
||||
specifiers:
|
||||
'@coral-xyz/anchor': ^0.26.0
|
||||
'@coral-xyz/anchor': ^0.27.0
|
||||
'@solana/web3.js': ^1.73.3
|
||||
'@switchboard-xyz/common': ^2.1.33
|
||||
'@switchboard-xyz/solana.js': workspace:*
|
||||
|
@ -265,7 +267,7 @@ importers:
|
|||
ts-node: ^10.4.0
|
||||
typescript: ^4.7
|
||||
dependencies:
|
||||
'@coral-xyz/anchor': 0.26.0
|
||||
'@coral-xyz/anchor': 0.27.0
|
||||
'@solana/web3.js': 1.73.3
|
||||
'@switchboard-xyz/common': 2.1.33
|
||||
'@switchboard-xyz/solana.js': link:../../javascript/solana.js
|
||||
|
@ -508,9 +510,46 @@ packages:
|
|||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/@coral-xyz/borsh/0.26.0_@solana+web3.js@1.73.0:
|
||||
/@coral-xyz/anchor/0.27.0:
|
||||
resolution: {integrity: sha512-+P/vPdORawvg3A9Wj02iquxb4T0C5m4P6aZBVYysKl4Amk+r6aMPZkUhilBkD6E4Nuxnoajv3CFykUfkGE0n5g==}
|
||||
engines: {node: '>=11'}
|
||||
dependencies:
|
||||
'@coral-xyz/borsh': 0.27.0_@solana+web3.js@1.73.3
|
||||
'@solana/web3.js': 1.73.3
|
||||
base64-js: 1.5.1
|
||||
bn.js: 5.2.1
|
||||
bs58: 4.0.1
|
||||
buffer-layout: 1.2.2
|
||||
camelcase: 6.3.0
|
||||
cross-fetch: 3.1.5
|
||||
crypto-hash: 1.3.0
|
||||
eventemitter3: 4.0.7
|
||||
js-sha256: 0.9.0
|
||||
pako: 2.0.4
|
||||
snake-case: 3.0.4
|
||||
superstruct: 0.15.5
|
||||
toml: 3.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/@coral-xyz/borsh/0.26.0_@solana+web3.js@1.73.3:
|
||||
resolution: {integrity: sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.68.0
|
||||
dependencies:
|
||||
'@solana/web3.js': 1.73.3
|
||||
bn.js: 5.2.1
|
||||
buffer-layout: 1.2.2
|
||||
dev: false
|
||||
|
||||
/@coral-xyz/borsh/0.27.0_@solana+web3.js@1.73.0:
|
||||
resolution: {integrity: sha512-tJKzhLukghTWPLy+n8K8iJKgBq1yLT/AxaNd10yJrX8mI56ao5+OFAKAqW/h0i79KCvb4BK0VGO5ECmmolFz9A==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.68.0
|
||||
dependencies:
|
||||
|
@ -519,8 +558,8 @@ packages:
|
|||
buffer-layout: 1.2.2
|
||||
dev: false
|
||||
|
||||
/@coral-xyz/borsh/0.26.0_@solana+web3.js@1.73.3:
|
||||
resolution: {integrity: sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==}
|
||||
/@coral-xyz/borsh/0.27.0_@solana+web3.js@1.73.3:
|
||||
resolution: {integrity: sha512-tJKzhLukghTWPLy+n8K8iJKgBq1yLT/AxaNd10yJrX8mI56ao5+OFAKAqW/h0i79KCvb4BK0VGO5ECmmolFz9A==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@solana/web3.js': ^1.68.0
|
||||
|
@ -2803,6 +2842,10 @@ packages:
|
|||
strip-final-newline: 2.0.0
|
||||
dev: true
|
||||
|
||||
/exponential-backoff/3.1.1:
|
||||
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
|
||||
dev: true
|
||||
|
||||
/external-editor/3.1.0:
|
||||
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
|
@ -26,16 +26,16 @@ url = "https://api.devnet.solana.com"
|
|||
startup_wait = 15000
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet programID
|
||||
address = "2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG"
|
||||
address = "SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet IDL
|
||||
address = "CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp"
|
||||
address = "Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet SbState
|
||||
address = "BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt"
|
||||
address = "CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet tokenVault
|
||||
address = "FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy"
|
||||
address = "7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie"
|
||||
|
||||
[[test.validator.clone]] # sbv2 SOL/USD Feed
|
||||
address="GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR"
|
||||
|
|
|
@ -19,5 +19,5 @@ cpi = ["no-entrypoint"]
|
|||
[dependencies]
|
||||
switchboard-v2 = { path = "../../rust/switchboard-v2" }
|
||||
# switchboard-v2 = { version = "^0.1.20" }
|
||||
anchor-lang = "^0.26.0"
|
||||
anchor-lang = "^0.27.0"
|
||||
solana-program = "~1.14.0"
|
|
@ -12,7 +12,7 @@
|
|||
"lint": "eslint --ext .js,.json,.ts 'src/**' --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@coral-xyz/anchor": "^0.27.0",
|
||||
"@solana/web3.js": "^1.73.3",
|
||||
"@switchboard-xyz/common": "^2.1.33",
|
||||
"@switchboard-xyz/solana.js": "workspace:*",
|
||||
|
|
|
@ -26,16 +26,16 @@ url = "https://api.devnet.solana.com"
|
|||
startup_wait = 15000
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet programID
|
||||
address = "2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG"
|
||||
address = "SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet IDL
|
||||
address = "CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp"
|
||||
address = "Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet SbState
|
||||
address = "BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt"
|
||||
address = "CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet tokenVault
|
||||
address = "FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy"
|
||||
address = "7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie"
|
||||
|
||||
[[test.validator.clone]] # sbv2 SOL/USD Feed
|
||||
address="GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR"
|
|
@ -19,6 +19,6 @@ cpi = ["no-entrypoint"]
|
|||
[dependencies]
|
||||
switchboard-v2 = { path = "../../rust/switchboard-v2" }
|
||||
# switchboard-v2 = { version = "0.1.20" }
|
||||
anchor-lang = "^0.26.0"
|
||||
anchor-lang = "^0.27.0"
|
||||
solana-program = "~1.14.0"
|
||||
bytemuck = "1.7.2"
|
|
@ -12,7 +12,7 @@
|
|||
"lint": "eslint --ext .js,.json,.ts 'src/**' --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@coral-xyz/anchor": "^0.27.0",
|
||||
"@solana/web3.js": "^1.73.3",
|
||||
"@switchboard-xyz/common": "^2.1.33",
|
||||
"@switchboard-xyz/solana.js": "workspace:*"
|
||||
|
|
|
@ -29,16 +29,16 @@ url = "https://api.devnet.solana.com"
|
|||
startup_wait = 15000
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet programID
|
||||
address = "2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG"
|
||||
address = "SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet IDL
|
||||
address = "CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp"
|
||||
address = "Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet SbState
|
||||
address = "BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt"
|
||||
address = "CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet tokenVault
|
||||
address = "FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy"
|
||||
address = "7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie"
|
||||
|
||||
[[test.validator.clone]] # sbv2 SOL/USD Feed
|
||||
address="GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR"
|
||||
|
|
|
@ -22,5 +22,5 @@ overflow-checks = true
|
|||
[dependencies]
|
||||
switchboard-v2 = { path = "../../rust/switchboard-v2" }
|
||||
# switchboard-v2 = { version = "^0.1.20" }
|
||||
anchor-lang = "^0.26.0"
|
||||
anchor-lang = "^0.27.0"
|
||||
solana-program = "^1.13.6"
|
|
@ -13,7 +13,7 @@
|
|||
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@coral-xyz/anchor": "^0.27.0",
|
||||
"@switchboard-xyz/common": "^2.1.33",
|
||||
"@switchboard-xyz/solana.js": "workspace:*"
|
||||
},
|
||||
|
|
|
@ -27,13 +27,13 @@ url = "https://api.devnet.solana.com"
|
|||
startup_wait = 15000
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet programID
|
||||
address = "2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG"
|
||||
address = "SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet IDL
|
||||
address = "CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp"
|
||||
address = "Fi8vncGpNKbq62gPo56G4toCehWNy77GgqGkTaAF5Lkk"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet SbState
|
||||
address = "BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt"
|
||||
address = "CyZuD7RPDcrqCGbNvLCyqk6Py9cEZTKmNKujfPi3ynDd"
|
||||
|
||||
[[test.validator.clone]] # sbv2 devnet tokenVault
|
||||
address = "FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy"
|
||||
address = "7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie"
|
||||
|
|
|
@ -9,8 +9,7 @@ crate-type = ["cdylib", "lib"]
|
|||
name = "anchor_vrf_parser"
|
||||
|
||||
[features]
|
||||
default = ["devnet"]
|
||||
devnet = ["switchboard-v2/devnet"]
|
||||
default = []
|
||||
no-entrypoint = []
|
||||
no-idl = []
|
||||
no-log-ix-name = []
|
||||
|
@ -19,7 +18,7 @@ cpi = ["no-entrypoint"]
|
|||
[dependencies]
|
||||
switchboard-v2 = { path = "../../rust/switchboard-v2" }
|
||||
# switchboard-v2 = { version = "^0.1.20" }
|
||||
anchor-lang = "^0.26.0"
|
||||
anchor-spl = "^0.26.0"
|
||||
anchor-lang = "^0.27.0"
|
||||
anchor-spl = "^0.27.0"
|
||||
solana-program = "~1.14.0"
|
||||
bytemuck = "1.7.2"
|
||||
|
|
|
@ -1,769 +0,0 @@
|
|||
#!/usr/bin/env ts-node-esm
|
||||
/* eslint-disable @typescript-eslint/no-loop-func */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import * as anchor from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token-v2";
|
||||
import {
|
||||
clusterApiUrl,
|
||||
Connection,
|
||||
ParsedTransactionWithMeta,
|
||||
PublicKey,
|
||||
} from "@solana/web3.js";
|
||||
import * as sbv2Utils from "@switchboard-xyz/sbv2-utils";
|
||||
import { toVrfStatusString } from "@switchboard-xyz/sbv2-utils";
|
||||
import * as sbv2 from "@switchboard-xyz/switchboard-v2";
|
||||
import chalk from "chalk";
|
||||
const yargs = require("yargs");
|
||||
const { hideBin } = require("yargs/helpers");
|
||||
// import yargs from "yargs";
|
||||
// import { hideBin } from "yargs/helpers";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { AnchorVrfParser, IDL } from "./target/types/anchor_vrf_parser";
|
||||
import { VrfClient } from "./client/accounts";
|
||||
import { PROGRAM_ID } from "./client/programId";
|
||||
|
||||
// const DEFAULT_MAINNET_RPC = "https://ssc-dao.genesysgo.net";
|
||||
// const DEFAULT_DEVNET_RPC = "https://devnet.genesysgo.net";
|
||||
const DEFAULT_LOCALNET_RPC = "http://localhost:8899";
|
||||
|
||||
const DEFAULT_COMMITMENT = "confirmed";
|
||||
|
||||
const VRF_REQUEST_AMOUNT = BigInt(2_000_000);
|
||||
|
||||
interface RequestRandomnessResult {
|
||||
success: boolean;
|
||||
status: string;
|
||||
counter: number;
|
||||
txRemaining: number;
|
||||
producer: string;
|
||||
alpha: string;
|
||||
alphaHex: string;
|
||||
proof?: string;
|
||||
proofHex?: string;
|
||||
proofBase64?: string;
|
||||
result: string;
|
||||
stage?: number;
|
||||
txs?: ParsedTransactionWithMeta[] | string[] | any[];
|
||||
}
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.scriptName("sbv2-vrf-example")
|
||||
.command(
|
||||
"create [queueKey]",
|
||||
"create a VRF client",
|
||||
(y: any) => {
|
||||
return y
|
||||
.positional("queueKey", {
|
||||
type: "string",
|
||||
describe: "publicKey of the oracle queue to create a VRF for",
|
||||
default: "F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy",
|
||||
})
|
||||
.option("maxResult", {
|
||||
description: "test",
|
||||
type: "number",
|
||||
});
|
||||
},
|
||||
async function (argv: any) {
|
||||
const { queueKey, rpcUrl, cluster, maxResult } = argv;
|
||||
|
||||
const { vrfClientProgram, switchboardProgram, payer, provider } =
|
||||
await loadCli(rpcUrl, cluster);
|
||||
|
||||
const vrfSecret = anchor.web3.Keypair.generate();
|
||||
|
||||
const [vrfClientKey, vrfClientBump] =
|
||||
anchor.utils.publicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("STATE"),
|
||||
vrfSecret.publicKey.toBytes(),
|
||||
payer.publicKey.toBytes(),
|
||||
],
|
||||
vrfClientProgram.programId
|
||||
);
|
||||
|
||||
const vrfIxCoder = new anchor.BorshInstructionCoder(vrfClientProgram.idl);
|
||||
const vrfClientCallback = {
|
||||
programId: vrfClientProgram.programId,
|
||||
accounts: [
|
||||
// ensure all accounts in updateResult are populated
|
||||
{ pubkey: vrfClientKey, isSigner: false, isWritable: true },
|
||||
{ pubkey: vrfSecret.publicKey, isSigner: false, isWritable: false },
|
||||
],
|
||||
ixData: vrfIxCoder.encode("updateResult", ""), // pass any params for instruction here
|
||||
};
|
||||
|
||||
const queueAccount = new sbv2.OracleQueueAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: new anchor.web3.PublicKey(queueKey),
|
||||
});
|
||||
const { unpermissionedVrfEnabled, authority, dataBuffer } =
|
||||
await queueAccount.loadData();
|
||||
|
||||
// Create Switchboard VRF and Permission account
|
||||
const vrfAccount = await sbv2.VrfAccount.create(switchboardProgram, {
|
||||
queue: queueAccount,
|
||||
callback: vrfClientCallback,
|
||||
authority: vrfClientKey, // vrf authority
|
||||
keypair: vrfSecret,
|
||||
});
|
||||
await sbv2Utils.sleep(2000);
|
||||
const { escrow } = await vrfAccount.loadData();
|
||||
console.log(sbv2Utils.chalkString("VRF Account", vrfAccount.publicKey));
|
||||
|
||||
const permissionAccount = await sbv2.PermissionAccount.create(
|
||||
switchboardProgram,
|
||||
{
|
||||
authority,
|
||||
granter: queueAccount.publicKey,
|
||||
grantee: vrfAccount.publicKey,
|
||||
}
|
||||
);
|
||||
// console.log(`Created Permission Account: ${permissionAccount.publicKey}`);
|
||||
|
||||
// If queue requires permissions to use VRF, check the correct authority was provided
|
||||
if (!unpermissionedVrfEnabled) {
|
||||
if (!payer.publicKey.equals(authority)) {
|
||||
throw new Error(
|
||||
`queue requires PERMIT_VRF_REQUESTS and wrong queue authority provided`
|
||||
);
|
||||
}
|
||||
|
||||
await permissionAccount.set({
|
||||
authority: payer,
|
||||
permission: sbv2.SwitchboardPermission.PERMIT_VRF_REQUESTS,
|
||||
enable: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Create VRF Client account
|
||||
await vrfClientProgram.methods
|
||||
.initState({
|
||||
maxResult: new anchor.BN(maxResult),
|
||||
})
|
||||
.accounts({
|
||||
state: vrfClientKey,
|
||||
vrf: vrfAccount.publicKey,
|
||||
payer: payer.publicKey,
|
||||
authority: payer.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
})
|
||||
.rpc();
|
||||
|
||||
console.log(sbv2Utils.chalkString("VRF Client", vrfClientKey));
|
||||
|
||||
console.log(`\nRun the following command to request randomness`);
|
||||
console.log(
|
||||
`\t${chalk.yellow(
|
||||
"npx sbv2-vrf-example request",
|
||||
vrfClientKey.toString()
|
||||
)}`
|
||||
);
|
||||
}
|
||||
)
|
||||
.command(
|
||||
"request [vrfClientKey]",
|
||||
"request randomness for a VRF client",
|
||||
(y: any) => {
|
||||
return y.positional("vrfClientKey", {
|
||||
type: "string",
|
||||
describe: "publicKey of the VRF client to request randomness for",
|
||||
demand: true,
|
||||
});
|
||||
},
|
||||
async function (argv: any) {
|
||||
const { vrfClientKey, rpcUrl, cluster } = argv;
|
||||
if (!vrfClientKey) {
|
||||
throw new Error(`Must provide vrfClientKey arguement`);
|
||||
}
|
||||
|
||||
const { vrfClientProgram, switchboardProgram, payer, provider } =
|
||||
await loadCli((rpcUrl as string) ?? "", (cluster as string) ?? "");
|
||||
|
||||
const vrfClient = await VrfClient.fetch(
|
||||
provider.connection,
|
||||
vrfClientProgram.programId,
|
||||
new anchor.web3.PublicKey(vrfClientKey)
|
||||
);
|
||||
if (!vrfClient) {
|
||||
throw new Error(
|
||||
`Failed to fetch VrfClient for account ${vrfClientKey}`
|
||||
);
|
||||
}
|
||||
|
||||
const vrfAccount = new sbv2.VrfAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfClient.vrf,
|
||||
});
|
||||
const vrfData = await vrfAccount.loadData();
|
||||
|
||||
const vrfEscrow = await spl.getAccount(
|
||||
provider.connection,
|
||||
vrfData.escrow,
|
||||
DEFAULT_COMMITMENT,
|
||||
spl.TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const queueAccount = new sbv2.OracleQueueAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfData.oracleQueue,
|
||||
});
|
||||
const queueData = await queueAccount.loadData();
|
||||
const mint = await queueAccount.loadMint();
|
||||
|
||||
const payerTokenAccount = await spl.getOrCreateAssociatedTokenAccount(
|
||||
provider.connection,
|
||||
payer,
|
||||
mint.address,
|
||||
payer.publicKey,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const tokensNeeded = VRF_REQUEST_AMOUNT - vrfEscrow.amount;
|
||||
|
||||
if (tokensNeeded > 0 && payerTokenAccount.amount < tokensNeeded) {
|
||||
throw new Error(
|
||||
`Payer token account has insufficient funds, need 2_000_000, current ${payerTokenAccount.amount}`
|
||||
);
|
||||
}
|
||||
|
||||
const [programStateAccount, programStateBump] =
|
||||
sbv2.ProgramStateAccount.fromSeed(switchboardProgram);
|
||||
|
||||
const [permissionAccount, permissionBump] =
|
||||
sbv2.PermissionAccount.fromSeed(
|
||||
switchboardProgram,
|
||||
queueData.authority,
|
||||
queueAccount.publicKey,
|
||||
vrfAccount.publicKey
|
||||
);
|
||||
|
||||
let ws: number | undefined = undefined;
|
||||
const waitForResultPromise = new Promise(
|
||||
(
|
||||
resolve: (result: anchor.BN) => void,
|
||||
reject: (reason: string) => void
|
||||
) => {
|
||||
ws = vrfClientProgram.provider.connection.onAccountChange(
|
||||
new anchor.web3.PublicKey(vrfClientKey),
|
||||
async (
|
||||
accountInfo: anchor.web3.AccountInfo<Buffer>,
|
||||
context: anchor.web3.Context
|
||||
) => {
|
||||
const clientState = VrfClient.decode(accountInfo.data);
|
||||
if (clientState.result.gt(new anchor.BN(0))) {
|
||||
resolve(clientState.result);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(async (result) => {
|
||||
console.log(
|
||||
sbv2Utils.chalkString("Client Result", result.toString(10))
|
||||
);
|
||||
if (ws) {
|
||||
await vrfClientProgram.provider.connection.removeAccountChangeListener(
|
||||
ws
|
||||
);
|
||||
}
|
||||
ws = undefined;
|
||||
return result;
|
||||
});
|
||||
|
||||
const requestTxnPromise = vrfClientProgram.methods
|
||||
.requestResult({
|
||||
switchboardStateBump: programStateBump,
|
||||
permissionBump,
|
||||
})
|
||||
.accounts({
|
||||
state: vrfClientKey,
|
||||
authority: payer.publicKey,
|
||||
switchboardProgram: switchboardProgram.programId,
|
||||
vrf: vrfAccount.publicKey,
|
||||
oracleQueue: queueAccount.publicKey,
|
||||
queueAuthority: queueData.authority,
|
||||
dataBuffer: queueData.dataBuffer,
|
||||
permission: permissionAccount.publicKey,
|
||||
escrow: vrfData.escrow,
|
||||
payerWallet: payerTokenAccount.address,
|
||||
payerAuthority: payer.publicKey,
|
||||
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
|
||||
programState: programStateAccount.publicKey,
|
||||
tokenProgram: spl.TOKEN_PROGRAM_ID,
|
||||
})
|
||||
.signers([payer, payer])
|
||||
.rpc()
|
||||
.then((txnSignature) => {
|
||||
console.log(
|
||||
`${chalk.yellow("Randomness Requested")}\t${txnSignature}`
|
||||
);
|
||||
});
|
||||
|
||||
let result: anchor.BN;
|
||||
try {
|
||||
result = await sbv2Utils.promiseWithTimeout(
|
||||
45_000,
|
||||
waitForResultPromise,
|
||||
new Error(`Timed out waiting for VRF Client callback`)
|
||||
);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
if (ws) {
|
||||
await vrfClientProgram.provider.connection.removeAccountChangeListener(
|
||||
ws
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
)
|
||||
.command(
|
||||
"loop [vrfClientKey] [numLoops]",
|
||||
"request randomness for a VRF client N times and record the results",
|
||||
(y: any) => {
|
||||
return y
|
||||
.positional("vrfClientKey", {
|
||||
type: "string",
|
||||
describe: "publicKey of the VRF client to request randomness for",
|
||||
demand: true,
|
||||
})
|
||||
.positional("numLoops", {
|
||||
type: "number",
|
||||
describe: "number of times to request randomness",
|
||||
default: 100,
|
||||
});
|
||||
},
|
||||
async function (argv: any) {
|
||||
const { vrfClientKey, rpcUrl, cluster, numLoops } = argv;
|
||||
if (!vrfClientKey) {
|
||||
throw new Error(`Must provide vrfClientKey arguement`);
|
||||
}
|
||||
|
||||
const startTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
const vrfClientPubkey = new anchor.web3.PublicKey(vrfClientKey);
|
||||
|
||||
const { vrfClientProgram, switchboardProgram, payer, provider } =
|
||||
await loadCli(rpcUrl as string, cluster as string);
|
||||
|
||||
const vrfClient = await VrfClient.fetch(
|
||||
provider.connection,
|
||||
vrfClientProgram.programId,
|
||||
vrfClientPubkey
|
||||
);
|
||||
if (!vrfClient) {
|
||||
throw new Error(
|
||||
`Failed to fetch VrfClient for account ${vrfClientKey}`
|
||||
);
|
||||
}
|
||||
|
||||
const vrfAccount = new sbv2.VrfAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfClient.vrf,
|
||||
});
|
||||
const vrfData = await vrfAccount.loadData();
|
||||
|
||||
const vrfEscrow = await spl.getAccount(
|
||||
provider.connection,
|
||||
vrfData.escrow,
|
||||
DEFAULT_COMMITMENT,
|
||||
spl.TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const queueAccount = new sbv2.OracleQueueAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfData.oracleQueue,
|
||||
});
|
||||
const queueData = await queueAccount.loadData();
|
||||
const mint = await queueAccount.loadMint();
|
||||
|
||||
const payerTokenAccount = await spl.getOrCreateAssociatedTokenAccount(
|
||||
provider.connection,
|
||||
payer,
|
||||
mint.address,
|
||||
payer.publicKey,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const tokensNeeded = VRF_REQUEST_AMOUNT - vrfEscrow.amount;
|
||||
|
||||
if (tokensNeeded > 0 && payerTokenAccount.amount < tokensNeeded) {
|
||||
throw new Error(
|
||||
`Payer token account has insufficient funds, need 2_000_000, current ${payerTokenAccount.amount}`
|
||||
);
|
||||
}
|
||||
|
||||
const [programStateAccount, programStateBump] =
|
||||
sbv2.ProgramStateAccount.fromSeed(switchboardProgram);
|
||||
|
||||
const [permissionAccount, permissionBump] =
|
||||
sbv2.PermissionAccount.fromSeed(
|
||||
switchboardProgram,
|
||||
queueData.authority,
|
||||
queueAccount.publicKey,
|
||||
vrfAccount.publicKey
|
||||
);
|
||||
|
||||
const requestInstruction = await vrfClientProgram.methods
|
||||
.requestResult({
|
||||
switchboardStateBump: programStateBump,
|
||||
permissionBump,
|
||||
})
|
||||
.accounts({
|
||||
state: vrfClientPubkey,
|
||||
authority: payer.publicKey,
|
||||
switchboardProgram: switchboardProgram.programId,
|
||||
vrf: vrfAccount.publicKey,
|
||||
oracleQueue: queueAccount.publicKey,
|
||||
queueAuthority: queueData.authority,
|
||||
dataBuffer: queueData.dataBuffer,
|
||||
permission: permissionAccount.publicKey,
|
||||
escrow: vrfData.escrow,
|
||||
payerWallet: payerTokenAccount.address,
|
||||
payerAuthority: payer.publicKey,
|
||||
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
|
||||
programState: programStateAccount.publicKey,
|
||||
tokenProgram: spl.TOKEN_PROGRAM_ID,
|
||||
})
|
||||
.signers([payer])
|
||||
.instruction();
|
||||
|
||||
const results: RequestRandomnessResult[] = [];
|
||||
let successes = 0;
|
||||
let failures = 0;
|
||||
try {
|
||||
for await (const i of Array.from(Array(numLoops).keys())) {
|
||||
let success = false;
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
await provider.sendAndConfirm(
|
||||
new anchor.web3.Transaction().add(requestInstruction),
|
||||
[payer]
|
||||
);
|
||||
// console.log("sent");
|
||||
break;
|
||||
} catch (error) {
|
||||
if (!error.message.includes("0x17b5")) {
|
||||
throw error;
|
||||
}
|
||||
await sbv2Utils.sleep(2500);
|
||||
}
|
||||
}
|
||||
|
||||
await awaitCallback(provider.connection, vrfClientPubkey, 90_000)
|
||||
.then(() => {
|
||||
success = true;
|
||||
successes++;
|
||||
|
||||
clearStatus();
|
||||
writeStatus(i, numLoops, successes, failures);
|
||||
})
|
||||
.catch(async (error) => {
|
||||
failures++;
|
||||
const vrf = await vrfAccount.loadData();
|
||||
|
||||
clearStatus();
|
||||
console.error(error.message);
|
||||
writeVrfState(vrf);
|
||||
writeStatus(i, numLoops, successes, failures);
|
||||
});
|
||||
} catch (error) {}
|
||||
|
||||
const vrf = await vrfAccount.loadData();
|
||||
const vrfStatus = sbv2Utils.toVrfStatusString(vrf.status);
|
||||
const result: RequestRandomnessResult = {
|
||||
success: vrfStatus === "StatusCallbackSuccess",
|
||||
counter: vrf.counter.toString(10),
|
||||
producer: vrf.builders[0].producer.toString(),
|
||||
status: vrfStatus,
|
||||
txRemaining: vrf.builders[0].txRemaining,
|
||||
alpha: `[${vrf.currentRound.alpha.slice(0, 32).toString()}]`,
|
||||
alphaHex: Buffer.from(vrf.currentRound.alpha.slice(0, 32)).toString(
|
||||
"hex"
|
||||
),
|
||||
proof: `[${vrf.builders[0].reprProof.slice(0, 80).toString()}]`,
|
||||
proofHex: Buffer.from(
|
||||
vrf.builders[0].reprProof.slice(0, 80)
|
||||
).toString("hex"),
|
||||
proofBase64: Buffer.from(
|
||||
vrf.builders[0].reprProof.slice(0, 80)
|
||||
).toString("base64"),
|
||||
result: `[${vrf.currentRound.result.toString()}]`,
|
||||
stage: vrf.builders[0].stage,
|
||||
txs:
|
||||
vrfStatus === "StatusCallbackSuccess"
|
||||
? undefined
|
||||
: await fetchTransactions(
|
||||
vrfAccount.program.provider.connection,
|
||||
vrfAccount.publicKey,
|
||||
15
|
||||
),
|
||||
};
|
||||
|
||||
results.push(result);
|
||||
saveResults(startTime, vrfClientKey, results);
|
||||
clearStatus();
|
||||
writeStatus(i, numLoops, successes, failures);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`GLOBAL: ${error}`);
|
||||
}
|
||||
|
||||
writeStatus(numLoops, numLoops, successes, failures);
|
||||
|
||||
saveResults(startTime, vrfClientKey, results);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
)
|
||||
.options({
|
||||
cluster: {
|
||||
type: "string",
|
||||
alias: "c",
|
||||
describe: "Solana cluster to interact with",
|
||||
options: ["devnet", "mainnet-beta", "localnet"],
|
||||
default: "devnet",
|
||||
demand: false,
|
||||
},
|
||||
rpcUrl: {
|
||||
type: "string",
|
||||
alias: "u",
|
||||
describe: "Alternative RPC URL",
|
||||
},
|
||||
})
|
||||
.help().argv;
|
||||
|
||||
function getRpcUrl(cluster: string): string {
|
||||
switch (cluster) {
|
||||
case "mainnet-beta":
|
||||
return clusterApiUrl("mainnet-beta");
|
||||
case "devnet":
|
||||
return clusterApiUrl("devnet");
|
||||
case "localnet":
|
||||
return DEFAULT_LOCALNET_RPC;
|
||||
default:
|
||||
throw new Error(`Failed to find RPC_URL for cluster ${cluster}`);
|
||||
}
|
||||
}
|
||||
|
||||
function writeVrfState(vrf: any) {
|
||||
console.log(`Status: ${toVrfStatusString(vrf.builders[0]?.status) ?? ""}`);
|
||||
console.log(`TxRemaining: ${vrf.builders[0]?.txRemaining ?? ""}`);
|
||||
console.log(
|
||||
`Alpha: [${vrf.currentRound.alpha
|
||||
.slice(0, 32)
|
||||
.map((value) => value.toString())}]`
|
||||
);
|
||||
console.log(
|
||||
`AlphaHex: ${Buffer.from(vrf.currentRound.alpha.slice(0, 32)).toString(
|
||||
"hex"
|
||||
)}`
|
||||
);
|
||||
console.log(
|
||||
`Proof: [${vrf.builders[0].reprProof
|
||||
.slice(0, 80)
|
||||
.map((value) => value.toString())}]`
|
||||
);
|
||||
console.log(
|
||||
`ProofHex: ${Buffer.from(vrf.builders[0].reprProof.slice(0, 80)).toString(
|
||||
"hex"
|
||||
)}`
|
||||
);
|
||||
console.log(
|
||||
`ProofBase64: ${Buffer.from(
|
||||
vrf.builders[0].reprProof.slice(0, 80)
|
||||
).toString("base64")}`
|
||||
);
|
||||
console.log(`Stage: ${vrf.builders[0].stage}`);
|
||||
}
|
||||
|
||||
function saveResults(
|
||||
timestamp: number,
|
||||
vrfClientKey: anchor.web3.PublicKey,
|
||||
results: RequestRandomnessResult[]
|
||||
) {
|
||||
if (results.length) {
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), `${vrfClientKey}_${timestamp}.json`),
|
||||
JSON.stringify(results, undefined, 2),
|
||||
"utf-8"
|
||||
);
|
||||
}
|
||||
|
||||
const errorResults = results.filter((result) => !result.success);
|
||||
|
||||
if (errorResults.length) {
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), `${vrfClientKey}_ERRORS_${timestamp}.json`),
|
||||
JSON.stringify(errorResults, undefined, 2),
|
||||
"utf-8"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCli(
|
||||
rpcUrl: string,
|
||||
cluster: string
|
||||
): Promise<{
|
||||
vrfClientProgram: anchor.Program<AnchorVrfParser>;
|
||||
switchboardProgram: anchor.Program;
|
||||
payer: anchor.web3.Keypair;
|
||||
provider: anchor.AnchorProvider;
|
||||
}> {
|
||||
if (cluster !== "mainnet-beta" && cluster !== "devnet") {
|
||||
throw new Error(
|
||||
`cluster must be mainnet-beta or devnet, cluster = ${cluster}`
|
||||
);
|
||||
}
|
||||
|
||||
process.env.ANCHOR_WALLET = sbv2Utils.getAnchorWalletPath();
|
||||
const url = rpcUrl || getRpcUrl(cluster);
|
||||
const envProvider = anchor.AnchorProvider.local(url);
|
||||
const provider = new anchor.AnchorProvider(
|
||||
new anchor.web3.Connection(url, {
|
||||
commitment: DEFAULT_COMMITMENT,
|
||||
}),
|
||||
envProvider.wallet,
|
||||
{
|
||||
commitment: DEFAULT_COMMITMENT,
|
||||
}
|
||||
);
|
||||
|
||||
const switchboardProgram = await sbv2.loadSwitchboardProgram(
|
||||
cluster,
|
||||
provider.connection,
|
||||
(provider.wallet as sbv2.AnchorWallet).payer,
|
||||
{
|
||||
commitment: DEFAULT_COMMITMENT,
|
||||
}
|
||||
);
|
||||
const payer = sbv2.programWallet(switchboardProgram);
|
||||
|
||||
// load VRF Client program
|
||||
const vrfClientProgram = new anchor.Program(
|
||||
IDL,
|
||||
PROGRAM_ID,
|
||||
provider,
|
||||
new anchor.BorshCoder(IDL)
|
||||
);
|
||||
|
||||
return {
|
||||
vrfClientProgram,
|
||||
switchboardProgram,
|
||||
payer,
|
||||
provider,
|
||||
};
|
||||
}
|
||||
|
||||
async function awaitCallback(
|
||||
connection: anchor.web3.Connection,
|
||||
vrfClientKey: anchor.web3.PublicKey,
|
||||
timeoutInterval: number,
|
||||
errorMsg = "Timed out waiting for VRF Client callback"
|
||||
) {
|
||||
let ws: number | undefined = undefined;
|
||||
const result: anchor.BN = await sbv2Utils
|
||||
.promiseWithTimeout(
|
||||
timeoutInterval,
|
||||
new Promise(
|
||||
(
|
||||
resolve: (result: anchor.BN) => void,
|
||||
reject: (reason: string) => void
|
||||
) => {
|
||||
ws = connection.onAccountChange(
|
||||
vrfClientKey,
|
||||
async (
|
||||
accountInfo: anchor.web3.AccountInfo<Buffer>,
|
||||
context: anchor.web3.Context
|
||||
) => {
|
||||
const clientState = VrfClient.decode(accountInfo.data);
|
||||
if (clientState.result.gt(new anchor.BN(0))) {
|
||||
resolve(clientState.result);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
new Error(errorMsg)
|
||||
)
|
||||
.finally(async () => {
|
||||
if (ws) {
|
||||
await connection.removeAccountChangeListener(ws);
|
||||
}
|
||||
ws = undefined;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const clearLastLine = () => {
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
};
|
||||
|
||||
function writeStatus(
|
||||
i: number,
|
||||
numLoops: number,
|
||||
successes: number,
|
||||
failures: number
|
||||
) {
|
||||
process.stdout.write(`# ${i} / ${numLoops}\n`);
|
||||
process.stdout.write(`${chalk.green("Success:", successes)} / ${i}\n`);
|
||||
process.stdout.write(`${chalk.red("Errors: ", failures)} / ${i}\n`);
|
||||
}
|
||||
|
||||
function clearStatus() {
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
}
|
||||
|
||||
async function fetchTransactions(
|
||||
connection: Connection,
|
||||
pubkey: PublicKey,
|
||||
numTransactions = 10
|
||||
): Promise<any[]> {
|
||||
const signatures = (
|
||||
await connection.getSignaturesForAddress(
|
||||
pubkey,
|
||||
{ limit: numTransactions },
|
||||
"confirmed"
|
||||
)
|
||||
).map((t) => t.signature);
|
||||
|
||||
console.log(`FETCHED ${signatures.length} transactions`);
|
||||
|
||||
let parsedTxns: ParsedTransactionWithMeta[] | null = null;
|
||||
while (!parsedTxns) {
|
||||
parsedTxns = await connection.getParsedTransactions(
|
||||
signatures,
|
||||
"confirmed"
|
||||
);
|
||||
|
||||
if (!parsedTxns || parsedTxns.length !== signatures.length) {
|
||||
await sbv2Utils.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
return parsedTxns.map((tx, i) => {
|
||||
return {
|
||||
signature: signatures[i],
|
||||
logs: tx.meta.logMessages,
|
||||
};
|
||||
});
|
||||
}
|
|
@ -7,25 +7,15 @@
|
|||
"url": "https://github.com/switchboard-xyz/sbv2-solana",
|
||||
"directory": "programs/anchor-vrf-parser"
|
||||
},
|
||||
"bin": {
|
||||
"sbv2-vrf-example": "./sbv2-vrf-example.sh"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.json,.ts 'src/**' --fix",
|
||||
"pubkey": "solana-keygen pubkey target/deploy/anchor_vrf_parser-keypair.json",
|
||||
"localnet": "npm run localnet:down || exit 0; solana-test-validator -q -r --ledger .anchor/test-ledger --mint $(solana-keygen pubkey ~/.config/solana/id.json) --bind-address 0.0.0.0 --url https://api.devnet.solana.com --rpc-port 8899 --clone 2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG `# programId` --clone J4CArpsbrZqu1axqQ4AnrqREs3jwoyA1M5LMiQQmAzB9 `# programDataAddress` --clone CKwZcshn4XDvhaWVH9EXnk3iu19t6t5xP2Sy2pD6TRDp `# idlAddress` --clone BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt `# programState` --clone FVLfR6C2ckZhbSwBzZY4CX7YBcddUSge5BNeGQv5eKhy `# switchboardVault` & npm run localnet:wait",
|
||||
"localnet:wait": "for attempt in {1..30}; do sleep 1; if curl -sS http://localhost:8899 -X POST -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1, \"method\":\"getBlockHeight\"}'; then echo ready; break; fi; done",
|
||||
"localnet:down": "kill -9 $(pgrep command solana-test-validator) || exit 0",
|
||||
"network:create": "sbv2 solana network create --keypair ~/.config/solana/id.json --configFile .switchboard/networks/default.config.json --schemaFile .switchboard/networks/default.json --cluster localnet --force",
|
||||
"network:start": "sbv2 solana network start --keypair ~/.config/solana/id.json --schemaFile .switchboard/networks/default.json --cluster localnet --nodeImage dev-v2-RC_01_05_23_20_01-beta",
|
||||
"network:start:dev": "VERBOSE=1 DEBUG=1 CHAIN=solana CLUSTER=localnet TASK_RUNNER_SOLANA_RPC=https://api.mainnet-beta.solana.com FS_PAYER_SECRET_PATH=~/.config/solana/id.json RPC_URL=http://localhost:8899 ORACLE_KEY=Ei4HcqRQtf6TfwbuRXKRwCtt8PDXhmq9NhYLWpoh23xp ts-node ../../../switchboard-oracle-v2/node/src/apps/oracle",
|
||||
"build": "node ../build.js anchor-vrf-parser 4wTeTACfwiXqqvy44bNBB3V2rFjmSTXVoEr4ZAYamJEN",
|
||||
"test": "npm run localnet && npm run network:create && npm run network:start & sleep 60 && anchor test --skip-local-validator",
|
||||
"test:dev": "npm run localnet && npm run network:create && npm run network:start:dev & sleep 15 && anchor test --skip-local-validator"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@coral-xyz/borsh": "^0.26.0",
|
||||
"@coral-xyz/anchor": "^0.27.0",
|
||||
"@coral-xyz/borsh": "^0.27.0",
|
||||
"@project-serum/borsh": "^0.2.5",
|
||||
"@solana/spl-token": "^0.3.6",
|
||||
"@solana/web3.js": "^1.73.3",
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
script_dir=$(dirname "$(readlink -f "$BASH_SOURCE")")
|
||||
# echo "$script_dir"
|
||||
|
||||
# /usr/bin/env node --enable-source-maps "$script_dir"/cli.js "$@"
|
||||
# "$script_dir"/node_modules/.bin/ts-node-esm "$script_dir"/cli.ts "$@"
|
||||
/usr/bin/env ts-node-esm "$script_dir"/cli.ts "$@"
|
|
@ -40,7 +40,7 @@ pub mod anchor_vrf_parser {
|
|||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
pub struct VrfClient {
|
||||
pub bump: u8,
|
||||
pub max_result: u64,
|
||||
|
|
|
@ -4,6 +4,7 @@ import "mocha";
|
|||
import * as anchor from "@coral-xyz/anchor";
|
||||
import { AnchorProvider } from "@coral-xyz/anchor";
|
||||
import {
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
|
||||
} from "@solana/web3.js";
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
QueueAccount,
|
||||
SwitchboardProgram,
|
||||
SwitchboardTestContextV2,
|
||||
SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE,
|
||||
types,
|
||||
} from "@switchboard-xyz/solana.js";
|
||||
|
||||
|
@ -34,15 +36,14 @@ describe("anchor-vrf-parser test", () => {
|
|||
const vrfSecret = anchor.web3.Keypair.generate();
|
||||
console.log(`VRF Account: ${vrfSecret.publicKey}`);
|
||||
|
||||
const [vrfClientKey, vrfClientBump] =
|
||||
anchor.utils.publicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("STATE"),
|
||||
vrfSecret.publicKey.toBytes(),
|
||||
payer.publicKey.toBytes(),
|
||||
],
|
||||
vrfClientProgram.programId
|
||||
);
|
||||
const [vrfClientKey, vrfClientBump] = PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("STATE"),
|
||||
vrfSecret.publicKey.toBytes(),
|
||||
payer.publicKey.toBytes(),
|
||||
],
|
||||
vrfClientProgram.programId
|
||||
);
|
||||
|
||||
const vrfIxCoder = new anchor.BorshInstructionCoder(vrfClientProgram.idl);
|
||||
const vrfClientCallback: Callback = {
|
||||
|
@ -66,7 +67,7 @@ describe("anchor-vrf-parser test", () => {
|
|||
);
|
||||
[queueAccount, queue] = await QueueAccount.load(
|
||||
switchboardProgram,
|
||||
"F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy"
|
||||
SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE
|
||||
);
|
||||
} else {
|
||||
switchboard = await SwitchboardTestContextV2.loadFromProvider(provider, {
|
||||
|
@ -216,7 +217,7 @@ describe("anchor-vrf-parser test", () => {
|
|||
const newVrfSecret = anchor.web3.Keypair.generate();
|
||||
|
||||
const [newVrfClientKey, newVrfClientBump] =
|
||||
anchor.utils.publicKey.findProgramAddressSync(
|
||||
PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("STATE"),
|
||||
newVrfSecret.publicKey.toBytes(),
|
||||
|
|
|
@ -11,7 +11,7 @@ name = "native_feed_parser"
|
|||
no-entrypoint = []
|
||||
|
||||
[dependencies]
|
||||
switchboard-v2 = { path = "../../rust/switchboard-v2", features = ["devnet"] }
|
||||
# switchboard-v2 = { version = "^0.1.20", features = ["devnet"] }
|
||||
switchboard-v2 = { path = "../../rust/switchboard-v2" }
|
||||
# switchboard-v2 = "^0.1.20"
|
||||
solana-program = "~1.14.0"
|
||||
anchor-lang = "^0.26.0"
|
||||
anchor-lang = "^0.27.0"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"test": "echo \"For workspace native-feed-parser, use the anchor:test script\" && exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@coral-xyz/anchor": "^0.27.0",
|
||||
"@solana/web3.js": "^1.73.3",
|
||||
"@switchboard-xyz/common": "^2.1.33",
|
||||
"@switchboard-xyz/solana.js": "workspace:*"
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
---
|
||||
|
||||
SwitchboardPy is the Python client for [Switchboard](https://docs.switchboard.xyz/introduction). It provides wrappers to help you to interact with the Switchboard V2 program on-chain.
|
||||
SwitchboardPy is the Python client for
|
||||
[Switchboard](https://docs.switchboard.xyz/introduction). It provides wrappers
|
||||
to help you to interact with the Switchboard V2 program on-chain.
|
||||
|
||||
Internally it uses [AnchorPy](https://kevinheavey.github.io/anchorpy/), an Anchor API implementation in Python.
|
||||
Internally it uses [AnchorPy](https://kevinheavey.github.io/anchorpy/), an
|
||||
Anchor API implementation in Python.
|
||||
|
||||
[![pypi](https://badgen.net/pypi/v/switchboardpy)](https://pypi.python.org/pypi/switchboardpy)
|
||||
[![twitter](https://badgen.net/twitter/follow/switchboardxyz)](https://twitter.com/switchboardxyz)
|
||||
|
@ -28,7 +31,7 @@ from switchboardpy import AggregatorAccount, AccountParams
|
|||
|
||||
# Devnet Program ID.
|
||||
SBV2_DEVNET_PID = PublicKey(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
|
||||
)
|
||||
|
||||
async def main():
|
||||
|
|
|
@ -8,7 +8,7 @@ from switchboardpy import AggregatorAccount, AccountParams
|
|||
|
||||
# Devnet Program ID.
|
||||
SBV2_DEVNET_PID = PublicKey(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
|
||||
)
|
||||
|
||||
async def main():
|
||||
|
|
|
@ -13,7 +13,7 @@ from switchboardpy.compiled import OracleJob
|
|||
|
||||
# Devnet Program ID.
|
||||
SBV2_DEVNET_PID = PublicKey(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
|
||||
)
|
||||
|
||||
# Mainnet-Beta Program ID.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
from solana.publickey import PublicKey
|
||||
|
||||
os.environ.setdefault('SWITCHBOARD_PID', '2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG')
|
||||
os.environ.setdefault('SWITCHBOARD_PID', 'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f')
|
||||
|
||||
|
||||
PROGRAM_ID = PublicKey(os.environ['SWITCHBOARD_PID'] or "SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f")
|
||||
|
|
|
@ -16,7 +16,7 @@ from .generated.accounts import SbState
|
|||
|
||||
# Devnet Program ID.
|
||||
SBV2_DEVNET_PID = PublicKey(
|
||||
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
|
||||
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
|
||||
)
|
||||
|
||||
# Mainnet-Beta Program ID.
|
||||
|
|
|
@ -83,7 +83,7 @@ async def test_create():
|
|||
queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -54,7 +54,7 @@ async def test_create():
|
|||
queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -53,7 +53,7 @@ async def test_create():
|
|||
queue = OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -68,7 +68,7 @@ async def test_create():
|
|||
queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
start_after=0,
|
||||
|
@ -134,7 +134,7 @@ async def test_create():
|
|||
oracle_queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -49,7 +49,7 @@ async def test_create():
|
|||
queue = OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -64,7 +64,7 @@ async def test_create():
|
|||
queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
start_after=0,
|
||||
|
@ -131,7 +131,7 @@ async def test_create():
|
|||
oracle_queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
)
|
||||
|
@ -140,7 +140,7 @@ async def test_create():
|
|||
await aggregator.open_round(AggregatorOpenRoundParams(OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
), payout_wallet=tokenAccount))
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ async def test_create():
|
|||
queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
start_after=0,
|
||||
|
@ -91,7 +91,7 @@ async def test_create():
|
|||
oracle_queue_account=OracleQueueAccount(
|
||||
AccountParams(
|
||||
program=program,
|
||||
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
|
||||
public_key=PublicKey("uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX")
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ from solana.rpc.async_api import AsyncClient
|
|||
from anchorpy import Program, Provider, Wallet
|
||||
from switchboardpy.program import ProgramStateAccount
|
||||
|
||||
ORACLE_QUEUE_STANDARD_DEVNET = 'F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy' # <-- new key | old key - 'B4yBQ3hYcjnrNLxUnauJqwpFJnjtm7s8gHybgkAdgXhQ';
|
||||
ORACLE_QUEUE_STANDARD_DEVNET = 'uPeRMdfPmrPqgRWSrjAnAkH78RqAhe5kXoW6vBYRqFX' # <-- new key | old key - 'B4yBQ3hYcjnrNLxUnauJqwpFJnjtm7s8gHybgkAdgXhQ';
|
||||
|
||||
|
||||
class SwitchboardProgram(object):
|
||||
|
|
|
@ -23,8 +23,8 @@ cpi = ["no-entrypoint"]
|
|||
devnet = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = "~0.26.0"
|
||||
anchor-spl = "~0.26.0"
|
||||
anchor-lang = "~0.27.0"
|
||||
anchor-spl = "~0.27.0"
|
||||
rust_decimal = "=1.26.1"
|
||||
solana-program = "^1.13.5"
|
||||
bytemuck = "1.7.2"
|
||||
|
|
|
@ -45,39 +45,6 @@ Or add the following line to your Cargo.toml:
|
|||
switchboard-v2 = "0.1.22"
|
||||
```
|
||||
|
||||
## Featuresc
|
||||
|
||||
| Feature | Description |
|
||||
| ------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| devnet | The devnet feature enables using the Switchboard Devnet Program ID instead of the Mainnet Program ID. |
|
||||
|
||||
Enable it in your Cargo.toml
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
switchboard-v2 = { version = "0.1.22", features = ["devnet"] }
|
||||
```
|
||||
|
||||
### Define Your Own Devnet Feature
|
||||
|
||||
You can also define your own devnet feature to dynamically swap the program IDs.
|
||||
|
||||
```toml
|
||||
[features]
|
||||
default = []
|
||||
devnet = ["switchboard-v2/devnet"]
|
||||
```
|
||||
|
||||
This allows you to build your program with a feature flag to automate devnet and
|
||||
mainnet builds.
|
||||
|
||||
```bash
|
||||
# Build with Mainnet Switchboard Program ID
|
||||
cargo build-bpf
|
||||
# Build with Devnet Switchboard Program ID
|
||||
cargo build-bpf --features devnet
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Aggregator
|
||||
|
@ -111,8 +78,8 @@ feed.check_confidence_interval(SwitchboardDecimal::from_f64(0.80))?;
|
|||
```
|
||||
|
||||
**Example(s)**:
|
||||
[anchor-feed-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/examples/programs/anchor-feed-parser/src/lib.rs),
|
||||
[native-feed-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/examples/programs/native-feed-parser/src/lib.rs)
|
||||
[anchor-feed-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/programs/anchor-feed-parser/src/lib.rs),
|
||||
[native-feed-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/programs/native-feed-parser/src/lib.rs)
|
||||
|
||||
#### Read Aggregator History
|
||||
|
||||
|
@ -146,7 +113,7 @@ let result = value[0] % 256000 as u128;
|
|||
```
|
||||
|
||||
**Example**:
|
||||
[anchor-vrf-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/examples/programs/anchor-vrf-parser/src/actions/update_result.rs)
|
||||
[anchor-vrf-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/programs/anchor-vrf-parser/src/actions/update_result.rs)
|
||||
|
||||
#### RequestRandomness CPI
|
||||
|
||||
|
@ -190,7 +157,7 @@ vrf_request_randomness.invoke_signed(
|
|||
```
|
||||
|
||||
**Example**:
|
||||
[anchor-vrf-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/examples/programs/anchor-vrf-parser/src/actions/request_result.rs)
|
||||
[anchor-vrf-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/programs/anchor-vrf-parser/src/actions/request_result.rs)
|
||||
|
||||
### Buffer Relayer Account
|
||||
|
||||
|
@ -223,7 +190,7 @@ msg!("Buffer string {:?}!", result_string);
|
|||
```
|
||||
|
||||
**Example**:
|
||||
[anchor-buffer-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/examples/programs/anchor-buffer-parser/src/lib.rs)
|
||||
[anchor-buffer-parser](https://github.com/switchboard-xyz/sbv2-solana/blob/main/programs/anchor-buffer-parser/src/lib.rs)
|
||||
|
||||
## Supported CPI Calls
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use anchor_lang::Discriminator;
|
|||
use rust_decimal::Decimal;
|
||||
use std::cell::Ref;
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub struct Hash {
|
||||
|
@ -13,7 +13,7 @@ pub struct Hash {
|
|||
pub data: [u8; 32],
|
||||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub struct AggregatorRound {
|
||||
|
@ -54,14 +54,14 @@ pub enum AggregatorResolutionMode {
|
|||
ModeRoundResolution = 0,
|
||||
ModeSlidingResolution = 1,
|
||||
}
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct SlidingResultAccountData {
|
||||
pub data: [SlidingWindowElement; 16],
|
||||
pub bump: u8,
|
||||
pub _ebuf: [u8; 512],
|
||||
}
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct SlidingWindowElement {
|
||||
|
@ -72,7 +72,7 @@ pub struct SlidingWindowElement {
|
|||
}
|
||||
|
||||
// #[zero_copy]
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct AggregatorAccountData {
|
||||
|
|
|
@ -104,7 +104,7 @@ impl Discriminator for BufferRelayerAccountData {
|
|||
const DISCRIMINATOR: [u8; 8] = [50, 35, 51, 115, 169, 219, 158, 52];
|
||||
}
|
||||
impl Owner for BufferRelayerAccountData {
|
||||
fn owner() -> solana_program::pubkey::Pubkey {
|
||||
fn owner() -> Pubkey {
|
||||
SWITCHBOARD_PROGRAM_ID
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct CrankRow {
|
||||
|
@ -13,7 +13,7 @@ pub struct CrankRow {
|
|||
unsafe impl Pod for CrankRow {}
|
||||
unsafe impl Zeroable for CrankRow {}
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct CrankAccountData {
|
||||
/// Name of the crank to store on-chain.
|
||||
|
|
|
@ -5,7 +5,7 @@ use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
|
|||
use rust_decimal::Decimal;
|
||||
use std::convert::{From, TryInto};
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
pub struct SwitchboardDecimal {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct AccountMetaZC {
|
||||
pub pubkey: Pubkey,
|
||||
|
@ -10,7 +10,7 @@ pub struct AccountMetaZC {
|
|||
pub is_writable: bool,
|
||||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct AccountMetaBorsh {
|
||||
|
@ -19,7 +19,7 @@ pub struct AccountMetaBorsh {
|
|||
pub is_writable: bool,
|
||||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct CallbackZC {
|
||||
/// The program ID of the callback program being invoked.
|
||||
|
@ -49,7 +49,7 @@ pub struct Callback {
|
|||
pub ix_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct VrfRound {
|
||||
/// The alpha bytes used to calculate the VRF proof.
|
||||
|
@ -101,7 +101,7 @@ impl std::fmt::Display for VrfStatus {
|
|||
}
|
||||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct EcvrfProofZC {
|
||||
pub Gamma: EdwardsPointZC, // RistrettoPoint
|
||||
|
@ -117,7 +117,7 @@ impl Default for EcvrfProofZC {
|
|||
/// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which
|
||||
/// represents an element of \\(\mathbb Z / \ell\\).
|
||||
#[allow(dead_code)]
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct Scalar {
|
||||
/// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the
|
||||
|
@ -158,7 +158,7 @@ pub struct FieldElement51(pub(crate) [u64; 5]);
|
|||
unsafe impl Pod for FieldElement51 {}
|
||||
unsafe impl Zeroable for FieldElement51 {}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct FieldElementZC {
|
||||
pub(crate) bytes: [u64; 5],
|
||||
|
@ -197,7 +197,7 @@ pub struct CompletedPoint {
|
|||
pub Z: FieldElement51,
|
||||
pub T: FieldElement51,
|
||||
}
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct CompletedPointZC {
|
||||
pub X: FieldElementZC,
|
||||
|
@ -243,7 +243,7 @@ pub struct EdwardsPoint {
|
|||
pub(crate) T: FieldElement51,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct EdwardsPointZC {
|
||||
pub(crate) X: FieldElementZC,
|
||||
|
@ -271,7 +271,7 @@ pub struct ProjectivePoint {
|
|||
pub Y: FieldElement51,
|
||||
pub Z: FieldElement51,
|
||||
}
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct ProjectivePointZC {
|
||||
pub(crate) X: FieldElementZC,
|
||||
|
@ -304,7 +304,7 @@ impl Into<ProjectivePoint> for ProjectivePointZC {
|
|||
}
|
||||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct EcvrfIntermediate {
|
||||
pub r: FieldElementZC,
|
||||
|
@ -317,7 +317,7 @@ unsafe impl Pod for EcvrfIntermediate {}
|
|||
unsafe impl Zeroable for EcvrfIntermediate {}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[repr(packed)]
|
||||
pub struct VrfBuilder {
|
||||
/// The OracleAccountData that is producing the randomness.
|
||||
|
|
|
@ -6,7 +6,7 @@ use bytemuck::{Pod, Zeroable};
|
|||
use std::cell::Ref;
|
||||
use superslice::*;
|
||||
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct AggregatorHistoryRow {
|
||||
|
|
|
@ -20,6 +20,7 @@ pub struct JobAccountData {
|
|||
pub total_spent: u64,
|
||||
/// Unix timestamp when the job was created on-chain.
|
||||
pub created_at: i64,
|
||||
pub is_initializing: u8,
|
||||
}
|
||||
|
||||
impl JobAccountData {}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct LeaseAccountData {
|
||||
/// Public key of the token account holding the lease contract funds until rewarded to oracles for successfully processing updates
|
||||
pub escrow: Pubkey, // Needed, maybe derived, key + "update_escrow"?
|
||||
/// Public key of the oracle queue that the lease contract is applicable for.
|
||||
pub queue: Pubkey,
|
||||
/// Public key of the aggregator that the lease contract is applicable for
|
||||
pub aggregator: Pubkey,
|
||||
/// Public key of the Solana token program ID.
|
||||
pub token_program: Pubkey,
|
||||
/// Whether the lease contract is still active.
|
||||
pub is_active: bool,
|
||||
/// Index of an aggregators position on a crank.
|
||||
pub crank_row_count: u32,
|
||||
/// Timestamp when the lease contract was created.
|
||||
pub created_at: i64,
|
||||
/// Counter keeping track of the number of updates for the given aggregator.
|
||||
pub update_count: u128,
|
||||
/// Public key of keypair that may withdraw funds from the lease at any time
|
||||
pub withdraw_authority: Pubkey,
|
||||
/// The PDA bump to derive the pubkey.
|
||||
pub bump: u8,
|
||||
// Reserved for future info.
|
||||
pub _ebuf: [u8; 255],
|
||||
}
|
||||
|
||||
impl LeaseAccountData {}
|
|
@ -9,6 +9,7 @@ pub mod ecvrf;
|
|||
pub mod error;
|
||||
pub mod history_buffer;
|
||||
pub mod job;
|
||||
pub mod lease;
|
||||
pub mod oracle;
|
||||
pub mod permission;
|
||||
pub mod queue;
|
||||
|
@ -25,6 +26,7 @@ pub use ecvrf::*;
|
|||
pub use error::SwitchboardError;
|
||||
pub use history_buffer::*;
|
||||
pub use job::*;
|
||||
pub use lease::*;
|
||||
pub use oracle::*;
|
||||
pub use permission::*;
|
||||
pub use queue::*;
|
||||
|
@ -47,20 +49,7 @@ pub const BUFFER_DISCRIMINATOR: &[u8] = b"BUFFERxx";
|
|||
/// Seed used to derive the SlidingWindow PDA.
|
||||
// const SLIDING_RESULT_SEED: &[u8] = b"SlidingResultAccountData";
|
||||
|
||||
/// Mainnet program id for Switchboard v2
|
||||
pub const SWITCHBOARD_V2_MAINNET: Pubkey = pubkey!("SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f");
|
||||
/// Program id for Switchboard v2
|
||||
pub const SWITCHBOARD_PROGRAM_ID: Pubkey = pubkey!("SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f");
|
||||
|
||||
/// Devnet program id for Switchboard v2
|
||||
pub const SWITCHBOARD_V2_DEVNET: Pubkey = pubkey!("2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG");
|
||||
|
||||
#[cfg(feature = "devnet")]
|
||||
/// Switchboard Program ID.
|
||||
pub const SWITCHBOARD_PROGRAM_ID: Pubkey = SWITCHBOARD_V2_DEVNET;
|
||||
#[cfg(not(feature = "devnet"))]
|
||||
/// Switchboard Program ID.
|
||||
pub const SWITCHBOARD_PROGRAM_ID: Pubkey = SWITCHBOARD_V2_MAINNET;
|
||||
|
||||
#[cfg(feature = "devnet")]
|
||||
declare_id!(SWITCHBOARD_V2_DEVNET);
|
||||
#[cfg(not(feature = "devnet"))]
|
||||
declare_id!(SWITCHBOARD_V2_MAINNET);
|
||||
declare_id!(SWITCHBOARD_PROGRAM_ID);
|
||||
|
|
|
@ -7,7 +7,7 @@ pub enum OracleResponseType {
|
|||
TypeDisagreement,
|
||||
TypeNoResponse,
|
||||
}
|
||||
#[zero_copy]
|
||||
#[zero_copy(unsafe)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct OracleMetrics {
|
||||
|
@ -31,7 +31,7 @@ pub struct OracleMetrics {
|
|||
pub total_late_response: u128,
|
||||
}
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct OracleAccountData {
|
||||
/// Name of the oracle to store on-chain.
|
||||
|
@ -51,8 +51,10 @@ pub struct OracleAccountData {
|
|||
pub queue_pubkey: Pubkey,
|
||||
/// Oracle track record.
|
||||
pub metrics: OracleMetrics,
|
||||
/// The PDA bump to derive the pubkey.
|
||||
pub bump: u8,
|
||||
/// Reserved for future info.
|
||||
pub _ebuf: [u8; 256],
|
||||
pub _ebuf: [u8; 255],
|
||||
}
|
||||
|
||||
impl OracleAccountData {}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub enum SwitchboardPermission {
|
|||
PermitVrfRequests = 1 << 2,
|
||||
}
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct PermissionAccountData {
|
||||
/// The authority that is allowed to set permissions for this account.
|
||||
|
@ -32,8 +32,10 @@ pub struct PermissionAccountData {
|
|||
/// unique expiration periods, BUT currently only one permission
|
||||
/// per account makes sense for the infra. Dont over engineer.
|
||||
pub expiration: i64,
|
||||
/// The PDA bump to derive the pubkey.
|
||||
pub bump: u8,
|
||||
/// Reserved for future info.
|
||||
pub _ebuf: [u8; 256],
|
||||
pub _ebuf: [u8; 255],
|
||||
}
|
||||
|
||||
impl PermissionAccountData {}
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::decimal::SwitchboardDecimal;
|
|||
use anchor_lang::prelude::*;
|
||||
use bytemuck::try_cast_slice_mut;
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct OracleQueueAccountData {
|
||||
/// Name of the queue to store on-chain.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct SbState {
|
||||
/// The account authority permitted to make account changes.
|
||||
|
@ -11,8 +11,10 @@ pub struct SbState {
|
|||
pub token_vault: Pubkey,
|
||||
/// The token mint used by the DAO.
|
||||
pub dao_mint: Pubkey,
|
||||
/// The PDA bump to derive the pubkey.
|
||||
pub bump: u8,
|
||||
/// Reserved for future info.
|
||||
pub _ebuf: [u8; 992],
|
||||
pub _ebuf: [u8; 991],
|
||||
}
|
||||
|
||||
impl SbState {}
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::cell::Ref;
|
|||
// VrfSetCallback
|
||||
// VrfClose
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct VrfAccountData {
|
||||
/// The current status of the VRF account.
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::cell::Ref;
|
|||
// VrfLiteRequestRandomnessParams
|
||||
// VrfLiteCloseParams
|
||||
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
#[repr(packed)]
|
||||
pub struct VrfLiteAccountData {
|
||||
/// The bump used to derive the SbState account.
|
||||
|
|
|
@ -18,7 +18,7 @@ pub struct VrfPoolRow {
|
|||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[account(zero_copy)]
|
||||
#[account(zero_copy(unsafe))]
|
||||
pub struct VrfPoolAccountData {
|
||||
/// ACCOUNTS
|
||||
pub authority: Pubkey, // authority can never be changed or else vrf accounts are useless
|
||||
|
|
Loading…
Reference in New Issue