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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions src/dryrun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import AlgodClient from './client/v2/algod/algod';
import {
Application,
ApplicationParams,
ApplicationStateSchema,
DryrunRequest,
DryrunSource,
} from './client/v2/algod/models/types';
import { SignedTransaction } from './transaction';
import { TransactionType } from './types/transactions';
import { encodeAddress, getApplicationAddress } from './encoding/address';

const defaultAppId = 1380011588;

// When writing the DryrunRequest object as msgpack the output needs to be the byte arrays not b64 string
interface AppParamsWithPrograms {
['approval-program']: string | Uint8Array;
['clear-state-program']: string | Uint8Array;
}

interface AppWithAppParams {
['params']: AppParamsWithPrograms;
}

function decodePrograms(ap: AppWithAppParams): AppWithAppParams {
// eslint-disable-next-line no-param-reassign
ap.params['approval-program'] = Buffer.from(
ap.params['approval-program'].toString(),
'base64'
);
// eslint-disable-next-line no-param-reassign
ap.params['clear-state-program'] = Buffer.from(
ap.params['clear-state-program'].toString(),
'base64'
);

return ap;
}

/**
* createDryrun takes an Algod Client (from algod.AlgodV2Client) and an array of Signed Transactions
* from (transaction.SignedTransaction) and creates a DryrunRequest object with relevant balances
* @param client - the AlgodClient to make requests against
* @param txns - the array of SignedTransaction to use for generating the DryrunRequest object
* @param protocolVersion - the string representing the protocol version to use
* @param latestTimestamp - the timestamp
* @returns the DryrunRequest object constructed from the SignedTransactions passed
*/
export async function createDryrun({
client,
txns,
protocolVersion,
latestTimestamp,
round,
sources,
}: {
client: AlgodClient;
txns: SignedTransaction[];
protocolVersion?: string;
latestTimestamp?: number | bigint;
round?: number | bigint;
sources?: DryrunSource[];
}): Promise<DryrunRequest> {
const appInfos = [];
const acctInfos = [];

const apps: number[] = [];
const assets: number[] = [];
const accts: string[] = [];

for (const t of txns) {
if (t.txn.type === TransactionType.appl) {
accts.push(encodeAddress(t.txn.from.publicKey));

if (t.txn.appAccounts)
accts.push(...t.txn.appAccounts.map((a) => encodeAddress(a.publicKey)));

if (t.txn.appForeignApps) apps.push(...t.txn.appForeignApps);

if (t.txn.appForeignAssets) assets.push(...t.txn.appForeignAssets);

// Create application,
if (t.txn.appIndex === 0) {
appInfos.push(
new Application(
defaultAppId,
new ApplicationParams({
creator: encodeAddress(t.txn.from.publicKey),
approvalProgram: t.txn.appApprovalProgram,
clearStateProgram: t.txn.appClearProgram,
localStateSchema: new ApplicationStateSchema(
t.txn.appLocalInts,
t.txn.appLocalByteSlices
),
globalStateSchema: new ApplicationStateSchema(
t.txn.appGlobalInts,
t.txn.appGlobalByteSlices
),
})
)
);
} else {
apps.push(t.txn.appIndex);
accts.push(getApplicationAddress(t.txn.appIndex));
}
}
}

// Dedupe and add creator to accts array
const assetPromises = [];
for (const assetId of [...new Set(assets)]) {
assetPromises.push(
client
.getAssetByID(assetId)
.do()
.then((assetInfo) => {
accts.push(assetInfo.params.creator);
})
);
}
// Wait for assets to finish since we append to accts array
await Promise.all(assetPromises);

// Dedupe and get app info for all apps
const appPromises = [];
for (const appId of [...new Set(apps)]) {
appPromises.push(
client
.getApplicationByID(appId)
.do()
.then((appInfo) => {
const ai = decodePrograms(appInfo as AppWithAppParams);
appInfos.push(ai);
})
);
}

const acctPromises = [];
for (const acct of [...new Set(accts)]) {
acctPromises.push(
client
.accountInformation(acct)
.do()
.then((acctInfo) => {
if ('created-apps' in acctInfo) {
// eslint-disable-next-line no-param-reassign
acctInfo['created-apps'] = acctInfo['created-apps'].map((app) =>
decodePrograms(app)
);
}
acctInfos.push(acctInfo);
})
);
}
await Promise.all([...appPromises, ...acctPromises]);

return new DryrunRequest({
txns: txns.map((st) => ({ ...st, txn: st.txn.get_obj_for_encoding() })),
accounts: acctInfos,
apps: appInfos,
latestTimestamp,
round,
protocolVersion,
sources,
});
}
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export {
} from './multisig';
export const LogicTemplates = LogicTemplatesCommonJSExport.default;

export * from './dryrun';
export * from './makeTxn';
export * from './transaction';
export * from './signer';
Expand Down