Skip to content

Commit

Permalink
Add support for checked tokenId, convert tokenId type to Field, addit…
Browse files Browse the repository at this point in the history
…ional feedback
  • Loading branch information
MartinMinkov committed Jul 19, 2022
1 parent 3b0ad43 commit 4b9b9f7
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 61 deletions.
8 changes: 4 additions & 4 deletions src/lib/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ async function fetchAccountInternal(
) {
const { publicKey, tokenId } = accountInfo;
let [response, error] = await makeGraphqlRequest(
accountQuery(publicKey, tokenId ?? getDefaultTokenId()),
accountQuery(publicKey, tokenId ?? getDefaultTokenId().toString()),
graphqlEndpoint,
config
);
Expand Down Expand Up @@ -163,7 +163,7 @@ type Account = {
publicKey: PublicKey;
nonce: UInt32;
balance: UInt64;
tokenId: string;
tokenId: Field;
tokenSymbol: string;
zkapp?: { appState: Field[] };
permissions?: Permissions;
Expand All @@ -176,7 +176,7 @@ type Account = {
type FlexibleAccount = {
publicKey: PublicKey | string;
nonce: UInt32 | string | number;
tokenId: string;
tokenId?: string;
tokenSymbol?: string;
balance?: UInt64 | string | number;
zkapp?: { appState: (Field | string | number)[] };
Expand Down Expand Up @@ -261,7 +261,7 @@ function stringifyAccount(account: FlexibleAccount): FetchedAccount {
zkapp?.appState.map((s) => s.toString()) ??
Array(ZkappStateLength).fill('0'),
balance: { total: balance?.toString() ?? '0' },
tokenId,
tokenId: tokenId ?? getDefaultTokenId().toString(),
tokenSymbol: tokenSymbol ?? '',
};
}
Expand Down
39 changes: 22 additions & 17 deletions src/lib/mina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ function createTransaction(
interface Mina {
transaction(sender: SenderSpec, f: () => void): Promise<Transaction>;
currentSlot(): UInt32;
getAccount(publicKey: PublicKey, tokenId?: string): Account;
getAccount(publicKey: PublicKey, tokenId?: Field): Account;
getNetworkState(): NetworkValue;
accountCreationFee(): UInt64;
sendTransaction(transaction: Transaction): TransactionId;
Expand Down Expand Up @@ -228,15 +228,14 @@ function LocalBlockchain({
},
getAccount(
publicKey: PublicKey,
tokenId: string = getDefaultTokenId()
tokenId: Field = getDefaultTokenId()
): Account {
let ledgerAccount = ledger.getAccount(
publicKey,
Ledger.fieldOfBase58(tokenId)
);
let ledgerAccount = ledger.getAccount(publicKey, tokenId);
if (ledgerAccount == undefined) {
throw new Error(
`getAccount: Could not find account for public key ${publicKey.toBase58()} with the tokenId ${tokenId}`
`getAccount: Could not find account for public key ${publicKey.toBase58()} with the tokenId ${Ledger.fieldToBase58(
tokenId
)}`
);
} else {
return {
Expand Down Expand Up @@ -301,13 +300,17 @@ function RemoteBlockchain(graphqlEndpoint: string): Mina {
},
getAccount(
publicKey: PublicKey,
tokenId: string = getDefaultTokenId()
tokenId: Field = getDefaultTokenId()
): Account {
if (currentTransaction?.fetchMode === 'test') {
Fetch.markAccountToBeFetched(publicKey, tokenId, graphqlEndpoint);
Fetch.markAccountToBeFetched(
publicKey,
Ledger.fieldToBase58(tokenId),
graphqlEndpoint
);
let account = Fetch.getCachedAccount(
publicKey,
tokenId,
Ledger.fieldToBase58(tokenId),
graphqlEndpoint
);
return account ?? dummyAccount(publicKey);
Expand All @@ -318,13 +321,15 @@ function RemoteBlockchain(graphqlEndpoint: string): Mina {
) {
let account = Fetch.getCachedAccount(
publicKey,
tokenId,
Ledger.fieldToBase58(tokenId),
graphqlEndpoint
);
if (account !== undefined) return account;
}
throw Error(
`getAccount: Could not find account for public key ${publicKey.toBase58()} with the tokenId ${tokenId}.\nGraphql endpoint: ${graphqlEndpoint}`
`getAccount: Could not find account for public key ${publicKey.toBase58()} with the tokenId ${Ledger.fieldToBase58(
tokenId
)}.\nGraphql endpoint: ${graphqlEndpoint}`
);
},
getNetworkState() {
Expand Down Expand Up @@ -398,12 +403,12 @@ let activeInstance: Mina = {
},
getAccount(
publicKey: PublicKey,
tokenId: string = getDefaultTokenId()
tokenId: Field = getDefaultTokenId()
): Account {
if (currentTransaction?.fetchMode === 'test') {
Fetch.markAccountToBeFetched(
publicKey,
tokenId,
Ledger.fieldToBase58(tokenId),
Fetch.defaultGraphqlEndpoint
);
return dummyAccount(publicKey);
Expand All @@ -414,7 +419,7 @@ let activeInstance: Mina = {
) {
let account = Fetch.getCachedAccount(
publicKey,
tokenId,
Ledger.fieldToBase58(tokenId),
Fetch.defaultGraphqlEndpoint
);
if (account === undefined)
Expand Down Expand Up @@ -484,7 +489,7 @@ function currentSlot(): UInt32 {
/**
* @return The account data associated to the given public key.
*/
function getAccount(publicKey: PublicKey, tokenId?: string): Account {
function getAccount(publicKey: PublicKey, tokenId?: Field): Account {
return activeInstance.getAccount(publicKey, tokenId);
}

Expand All @@ -498,7 +503,7 @@ function getNetworkState() {
/**
* @return The balance associated to the given public key.
*/
function getBalance(publicKey: PublicKey, tokenId?: string) {
function getBalance(publicKey: PublicKey, tokenId?: Field) {
return activeInstance.getAccount(publicKey, tokenId).balance;
}

Expand Down
44 changes: 24 additions & 20 deletions src/lib/party.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { PrivateKey, PublicKey } from './signature';
import { UInt64, UInt32, Int64 } from './int';
import * as Mina from './mina';
import { SmartContract } from './zkapp';
import { withContextAsync } from './global-context';
import { inCheckedComputation, withContextAsync } from './global-context';
import * as Precondition from './precondition';
import { Proof } from './proof_system';
import { emptyHashWithPrefix, hashWithPrefix, prefixes } from './hash';
Expand Down Expand Up @@ -242,7 +242,7 @@ let Permissions = {
}),
};

const getDefaultTokenId = () => Ledger.fieldToBase58(Field.one);
const getDefaultTokenId = () => Field.one;

type Event = Field[];

Expand Down Expand Up @@ -356,11 +356,11 @@ const Body = {
return {
publicKey,
update: Body.noUpdate(),
tokenId: Ledger.fieldOfBase58(getDefaultTokenId()),
tokenId: getDefaultTokenId(),
balanceChange: Int64.zero,
events: Events.empty(),
sequenceEvents: Events.empty(),
caller: Ledger.fieldOfBase58(getDefaultTokenId()),
caller: getDefaultTokenId(),
callData: Field.zero, // TODO new MerkleList(),
callDepth: 0,
preconditions: Preconditions.ignoreAll(),
Expand Down Expand Up @@ -504,37 +504,41 @@ type UnfinishedSignature = undefined | LazySignature | string;
type LazyControl = Control | LazySignature | LazyProof;

class Token {
readonly id: string;
readonly parentTokenId: string;
readonly id: Field;
readonly parentTokenId: Field;
readonly tokenOwner: PublicKey;

constructor(options: { tokenOwner: PublicKey; parentTokenId?: string }) {
constructor(options: { tokenOwner: PublicKey; parentTokenId?: Field }) {
let { tokenOwner, parentTokenId } = options ?? {};

// Reassign to default tokenId if undefined
parentTokenId ??= getDefaultTokenId();

// Check if we can deserialize the parentTokenId
try {
Ledger.fieldOfBase58(parentTokenId);
Ledger.fieldToBase58(parentTokenId);
} catch (e) {
throw new Error(`Invalid parentTokenId\n(error: ${(<Error>e).message})`);
throw new Error(
`Invalid parentTokenId\n(error: ${(e as Error).message})`
);
}

// Check if we can create a custom tokenId
try {
Ledger.customTokenID(tokenOwner, Ledger.fieldOfBase58(parentTokenId));
Ledger.customTokenId(tokenOwner, parentTokenId);
} catch (e) {
throw new Error(
`Could not create a custom token id:\n(error: ${(<Error>e).message})`
`Could not create a custom token id:\n(error: ${(e as Error).message})`
);
}

this.parentTokenId = parentTokenId;
this.tokenOwner = tokenOwner;
this.id = Ledger.customTokenID(
tokenOwner,
Ledger.fieldOfBase58(this.parentTokenId)
);
if (inCheckedComputation()) {
this.id = Ledger.customTokenIdChecked(tokenOwner, this.parentTokenId);
} else {
this.id = Ledger.customTokenId(tokenOwner, this.parentTokenId);
}
}
}

Expand Down Expand Up @@ -573,7 +577,7 @@ class Party {
return new (Party as any)(body, authorization, party.isSelf);
}

token(tokenId?: string) {
token(tokenId?: Field) {
let thisParty = this;
let customToken = new Token({
tokenOwner: thisParty.body.publicKey,
Expand Down Expand Up @@ -829,8 +833,8 @@ class Party {
static createUnsigned(
publicKey: PublicKey,
options?: {
caller?: string;
tokenId?: string;
caller?: Field;
tokenId?: Field;
callDepth?: number;
useFullCommitment?: Bool;
}
Expand All @@ -846,8 +850,8 @@ class Party {
const pk = publicKey;
const body: Body = Body.keepAll(pk);
const { caller, tokenId, callDepth, useFullCommitment } = options ?? {};
body.caller = caller ? Ledger.fieldOfBase58(caller) : body.caller;
body.tokenId = tokenId ? Ledger.fieldOfBase58(tokenId) : body.tokenId;
body.caller = caller ?? body.caller;
body.tokenId = tokenId ?? body.tokenId;
body.callDepth = callDepth ?? body.callDepth;
body.useFullCommitment = useFullCommitment ?? body.useFullCommitment;

Expand Down
2 changes: 1 addition & 1 deletion src/lib/precondition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ To write a correct circuit, you must avoid any dependency on the concrete value
let value: U;
if (accountOrNetwork === 'account') {
let address = party.body.publicKey;
let account: any = Mina.getAccount(address, getDefaultTokenId());
let account: any = Mina.getAccount(address, party.body.tokenId);
value = account[key];
if (value === undefined)
throw Error(
Expand Down
2 changes: 1 addition & 1 deletion src/lib/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function createState<T>(): InternalStateType<T> {
let address: PublicKey = this._contract.instance.address;
let { account } = await fetchAccount({
publicKey: address,
tokenId: getDefaultTokenId(),
tokenId: getDefaultTokenId().toString(),
});
if (account === undefined) return undefined;
let stateAsFields: Field[];
Expand Down
15 changes: 2 additions & 13 deletions src/lib/token.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Field,
shutdown,
isReady,
State,
Expand Down Expand Up @@ -144,22 +145,10 @@ describe('Token', () => {
});

it('should create a valid token with a different parentTokenId', async () => {
const newTokenId = Ledger.customTokenID(
tokenAccount1,
Ledger.fieldOfBase58(zkapp.token().id)
);
const newTokenId = Ledger.customTokenId(tokenAccount1, zkapp.token().id);
expect(newTokenId).toBeDefined();
});

it('should error if passing in an invalid parentTokenId', async () => {
expect(() => {
new Token({
tokenOwner: zkappAddress,
parentTokenId: 'invalid',
});
}).toThrow();
});

it('should error if passing in an invalid tokenSymbol', async () => {
await Mina.transaction(feePayer, () => {
zkapp.setInvalidTokenSymbol();
Expand Down
2 changes: 1 addition & 1 deletion src/lib/zkapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ class SmartContract {
return this.self.network;
}

token(tokenId?: string) {
token(tokenId?: Field) {
return this.self.token(tokenId);
}

Expand Down
5 changes: 2 additions & 3 deletions src/snarky.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { TokenId } from 'snarky/parties-leaves-json';

export {
Field,
Bool,
Expand Down Expand Up @@ -757,7 +755,8 @@ declare class Ledger {
i: number
): string;

static customTokenID(publicKey: { g: Group }, tokenId: Field): string;
static customTokenId(publicKey: { g: Group }, tokenId: Field): Field;
static customTokenIdChecked(publicKey: { g: Group }, tokenId: Field): Field;
static createTokenAccount(publicKey: { g: Group }, tokenId: Field): string;

static publicKeyToString(publicKey: { g: Group }): string;
Expand Down
6 changes: 5 additions & 1 deletion src/snarky/snarky-class-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,11 @@
"type": "function"
},
{
"name": "customTokenID",
"name": "customTokenId",
"type": "function"
},
{
"name": "customTokenIdChecked",
"type": "function"
},
{
Expand Down

0 comments on commit 4b9b9f7

Please sign in to comment.