contracts/tokens/fcpt.js
/**
 * 
 * A PST token model compatible with the
 * Foreign Call Protocol standards (FCP)
 * 
 * FCP spec: 
 *      https://www.notion.so/Foreign-Call-Protocol-Specification-61e221e5118a40b980fcaade35a2a718
 * 
 * 
 * NOTICE:
 * 
 * This contract is used only as token for testing
 * purposes. It covers the basics of a PST functionalities only.
 * 
 * 
**/
A PST token model compatible with the
Foreign Call Protocol standards (FCP)





This contract is used only as token for testing
purposes. It covers the basics of a PST functionalities only.
A PST token model compatible with the Foreign Call Protocol standards ( FCP ) This contract is used only as token for testing purposes . It covers the basics of a PST functionalities only .
async function handle (state, action) {
  const balances = state.balances;
  const invocations = state.invocations;
  const input = action.input;
  const caller = action.caller;
  if (input.function == "transfer") {
    const target = input.target;
    const qty = input.qty;
    if (!Number.isInteger(qty)) {
      throw new ContractError(`Invalid value for "qty". Must be an integer`);
    }
    if (!target) {
      throw new ContractError(`No target specified`);
    }
    if (qty <= 0 || caller == target) {
      throw new ContractError("Invalid token transfer");
    }
    if (balances[caller] < qty) {
      throw new ContractError(`Caller balance not high enough to send ${qty} token(s)!`);
    }
    balances[caller] -= qty;
    if (target in balances) {
      balances[target] += qty;
    } else {
      balances[target] = qty;
    }
    return { state };
  }
  if (input.function == "balance") {
    const target = input.target;
    const ticker = state.ticker;
    if (typeof target !== "string") {
      throw new ContractError(`Must specificy target to get balance for`);
    }
    if (typeof balances[target] !== "number") {
      throw new ContractError(`Cannnot get balance, target does not exist`);
    }
    return { result: { target, ticker, balance: balances[target] } };
  }
  if (input.function === "invoke") {
    if (!input.invocation) {
      throw new ContractError(`Missing function invocation`);
    }
    if (!input.foreignContract) {
      throw new ContractError(`Missing foreign contract`);
    }
    state.foreignCalls.push({
      contract: input.foreignContract,
      input: input.invocation
    });
    return { state };
  }
  if (input.function === "readOutbox") {
    if (!input.contract) {
      throw new ContractError(`Missing contract to invoke`);
    }
    const foreignState = await SmartWeave.contracts.readContractState(input.contract);
    if (!foreignState.foreignCalls) {
      throw new ContractError(`Contract is missing support for foreign calls`);
    }
    if (foreignState.foreignCalls[parseInt(input.id)].contract !== SmartWeave.contract.id) {
      throw new ContractError(`This contract is not the target contract chosen in the invocation`);
    }
    const invocation = foreignState.foreignCalls[input.id].input;
    const foreignCall = SmartWeave.transaction.id;
    if (invocations.includes(foreignCall)) {
      throw new ContractError(`Contract invocation already exists`);
    }
    const foreignAction = action;
    foreignAction.caller = input.contract;
    foreignAction.input = invocation;
    const resultState = await handle(state, foreignAction);
    invocations.push(foreignCall);
    return resultState;
  }
  throw new ContractError(`No function supplied or function not recognised: "${input.function}"`);
}
async function handle ( state , action ) { const balances = state . balances ; const invocations = state . invocations ; const input = action . input ; const caller = action . caller ; if ( input . function == "transfer" ) { const target = input . target ; const qty = input . qty ; if ( ! Number . isInteger ( qty ) ) { throw new ContractError ( ` ` ) ; } if ( ! target ) { throw new ContractError ( ` ` ) ; } if ( qty <= 0 || caller == target ) { throw new ContractError ( "Invalid token transfer" ) ; } if ( balances [ caller ] < qty ) { throw new ContractError ( ` ${ qty } ` ) ; } balances [ caller ] -= qty ; if ( target in balances ) { balances [ target ] += qty ; } else { balances [ target ] = qty ; } return { state } ; } if ( input . function == "balance" ) { const target = input . target ; const ticker = state . ticker ; if ( typeof target !== "string" ) { throw new ContractError ( ` ` ) ; } if ( typeof balances [ target ] !== "number" ) { throw new ContractError ( ` ` ) ; } return { result : { target , ticker , balance : balances [ target ] } } ; } if ( input . function === "invoke" ) { if ( ! input . invocation ) { throw new ContractError ( ` ` ) ; } if ( ! input . foreignContract ) { throw new ContractError ( ` ` ) ; } state . foreignCalls . push ( { contract : input . foreignContract , input : input . invocation } ) ; return { state } ; } if ( input . function === "readOutbox" ) { if ( ! input . contract ) { throw new ContractError ( ` ` ) ; } const foreignState = await SmartWeave . contracts . readContractState ( input . contract ) ; if ( ! foreignState . foreignCalls ) { throw new ContractError ( ` ` ) ; } if ( foreignState . foreignCalls [ parseInt ( input . id ) ] . contract !== SmartWeave . contract . id ) { throw new ContractError ( ` ` ) ; } const invocation = foreignState . foreignCalls [ input . id ] . input ; const foreignCall = SmartWeave . transaction . id ; if ( invocations . includes ( foreignCall ) ) { throw new ContractError ( ` ` ) ; } const foreignAction = action ; foreignAction . caller = input . contract ; foreignAction . input = invocation ; const resultState = await handle ( state , foreignAction ) ; invocations . push ( foreignCall ) ; return resultState ; } throw new ContractError ( ` ${ input . function } ` ) ; }
A PST token model compatible with the
 Foreign Call Protocol standards (FCP)
A PST token model compatible with the Foreign Call Protocol standards ( FCP )