Skip to content

Commit

Permalink
fixedBid contract history can now be fetched (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefdegroot authored Jun 8, 2023
1 parent b4c8a6d commit 0615c7e
Show file tree
Hide file tree
Showing 10 changed files with 935 additions and 31 deletions.
251 changes: 249 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,253 @@ Planned future expansion:

The interface gets the parameters for the specific contract function and returns an array of transactions that only need to be signed before they can be committed to the network.

Contracts:
## Contracts:
- [Fixed bid](src/interface/fixedBid/README.md)
- [ASA currency revenue sink](src/interface/acRevenueSink/README.md)
- [ASA currency revenue sink](src/interface/acRevenueSink/README.md)

## Documenation
- [Installation](#installation)
- [Setup](#setup)
- [Options](#options)
- [Verify Transactions](#verify-transactions)
- [Transaction Formatter](#verify-transactions)
- [Grouped Transactions](#grouped-transactions)
- [Payment Units](#payment-units)


> **Warning**
> Please note that all payment amounts in this package should be passed in as the base unit of the token. For details see: [Payment Units](#payment-units)
# Installation

```bash
npm install @dartroom/contracts
```

# Setup

You only need a connection to an Algorand [Indexer](https://github.com/algorand/indexer) and [Node](https://github.com/algorand/go-algorand) to get started.

```ts
import { Contracts } from "@dartroom/contracts"

const contracts = new Conctract({
indexer: {
token: "",
portNet: "",
baseServer: "",
},
algod: {
token: "",
portNet: "",
baseServer: "",
},
signature: false,
authAddress: false,
})
```

## Options

#### **`minBalanceFee?: number`**

Overwrite the default min balance fee for ASA opt-ins. Only use this if the min balance fee changes from the current default of 0.1 Algo in the future.

#### **`extendedTransactionFormat?: boolean`**

Extends the return format of the contract interaction functions. When enabled, the functions will not only return the standard AlgoSDK transaction format but add additional fields to make working with the transactions easier in a server + client-side setup.

#### **`serverSecret?: string`**

If a secret is provided and the `extendedTransactionFormat` is enabled, the return format will include a signed hash based on the `txID` signed with the secret. This will enable the system on the server to verify that the submitted transactions from the client were provided by the server and have not been altered in the client.

#### **`transactionBlobEncoding?: "Uint8Array" | "Base64"`**

If `extendedTransactionFormat` is enabled, this option will change the encoding of the original transaction in the `blob` field.

#### **`authAddress: boolean`**

Include the `authAddress` field in the transaction array return format. Some wallet apps and extensions, such as the AlgoSigner, require the auth field to support the use of rekeyed accounts.

#### **`signature: string`**

Include the server signature in the transaction array return format. The signature will be a hash of the txID signed with the server secret. The signature can be used to validate that a transaction sent back from the client was proposed by the server.

Note that you must provide a `serverSecret` to create server signatures.

# Verify Transactions

```ts
import { Contracts } from "@dartroom/contracts"

const contracts = new Conctract({
indexer: {
token: "",
portNet: "",
baseServer: "",
},
algod: {
token: "",
portNet: "",
baseServer: "",
},
extendedTransactionFormat: true,
serverSecret: serverSecret,
transactionBlobEncoding: 'Uint8Array',
authAddress: true,
signature: true
})

// Request the transactions to perform a specific action on the contract.
const unsignedTxns = await contracts.fixedBid.buy({
appId: 210217503,
nNFTs: 2,
buyerAddress: "FSQW3UTLB5IZ32ZE35MUDPNNAXHCBGMGAKXHR3ENQ5JMT43IB3BET7WPDE"
})

// Normally the unsigned transaction would be sent to the client from the server where they would be signed.For this example, the transactions get signed on the server (which, in practice, makes the server secret setup unnecessary as the transaction would never leave the server).
// All the extended transaction information must be returned from the server for this setup to work. When using a transaction signer, only sign the `blob` property and return it with the other transaction info.
// When using the Dartroom package, this is not necessary. With that setup, you can provide the full extended transaction, and the sign function will return the full extended transaction info.
const signedTxns = txns.map((txn) => {
return {
...txn,
blob: decodeUnsignedTransaction(txn.blob).signTxn(sellerAccount.sk)
}
})

// When receiving the transactions back from the client, the `verifyTxns` function can be used to verify the signature. The transaction will be hashed again and compared to the attached signature. If they are not equal or the signature field is missing, the function will throw an error. This way the server signature can be used to guard API routes from submitting transactions that where not proposed by your API.
contracts.verifyTxns(signedTxns)
```

# Transaction Formatter

```ts
import { Contracts } from "@dartroom/contracts"
import algosdk from "algosdk"

const contracts = new Conctract({
indexer: {
token: "",
portNet: "",
baseServer: "",
},
algod: {
token: "",
portNet: "",
baseServer: "",
},
extendedTransactionFormat: true,
serverSecret: serverSecret,
transactionBlobEncoding: 'Uint8Array',
authAddress: true,
signature: true
})

const address = "FSQW3UTLB5IZ32ZE35MUDPNNAXHCBGMGAKXHR3ENQ5JMT43IB3BET7WPDE"

// For simplicity, we use the `algod` instance generated by the contract setup, but this is just a standard `algod` instance. A separate `algod` instance would work the exact same way.
const account = await contracts.algod.accountInformation(address).do()
let params = await contracts.algod.getTransactionParams().do()

params.flatFee = true
params.fee = 1000

// The first step is to create a new transaction format instance. Every new transaction (group) will require a new instance.
const formater = contracts.newTxnFormatter()

// All transactions are passed into the push function with the additional info for the extended transaction format.
formater.push({
description: "Send a 0 Algo transaction to yourself to verify your account.",
txn: algosdk.makePaymentTxnWithSuggestedParams(
address,
address,
0,
undefined,
undefined,
params
),
signers: [address],
authAddress: account["auth-addr"] || address,
})

// When all transactions are pushed into the formatter, you can get the formatted transactions back by calling `getTxns()`.
// Calling `getTxns()` will convert the `blob` field and attach the `signature` based on the Contract options.
const tnxs = formater.getTxns()
```

## Grouped Transactions

If more than one transaction is pushed to the transaction formatter, the `assignGroupID()` function should be called. This function will generate a groupId for the transactions, which will create what is known as an [Atomic Transfer](https://developer.algorand.org/docs/get-details/atomic_transfers/) on Algorand. Atomic Transfer groups can include a maximum of 16 transactions. If more than 16 transactions need to be submitted, the group should be split among multiple transaction fromatters.

```ts
txnFormater.assignGroupID()

const tnxs = formater.getTxns()
```

# Payment Units

All Algorand transactions, and by extension contracts, work with integers only. The Algorand token, and all ASA tokens, have a specified decimals property that needs to be used to convert any float values back to integers for use in transactions.

The base unit conversion is not handled by the contract package to remove unnecessary Indexer calls. If you have a specific set of tokens that are allowed to be used on your platform, you can store the decimals statically, such as in the example below.
The `ASAToBaseInt` and `ASAToFloat` functions can be used to convert the amounts between the contract system and any frontend system you may use.

```ts
const tokens: Array<ASA> = [
{
index: 0,
unitName: "ALGO",
assetName: "ALGO",
decimals: 6,
},
{
index: 31566704,
unitName: "USDC",
assetName: "USDC",
decimals: 6,

},
{
index: 386195940,
unitName: "goETH",
assetName: "goETH",
decimals: 8,
}
]

function ASAToBaseInt (asa: ASA, amount: number) {
return amount * (10 ** asa.decimals)
}

function ASAToFloat (asa: ASA, amount: number) {
return amount / (10 ** asa.decimals)
}

const unitName = "USDC"
const asa = tokens.find((token) => token.unitName === unitName)

const txns = await contracts.fixedBid.updatePrice({
appId: 210217503,
unitPrice: ASAToBaseInt(asa, set.price)
})
```

If all currencies are allowed on the platform, then the decimal settings can be fetched from the Indexer or Node.

```ts
const asaIndex = 386195940
const asaInfo = await algod.getAssetByID(386195940).do()

function ASAToBaseInt (asaInfo: ASAInfo, amount: number) {
return amount * (10 ** asaInfo.params.decimals)
}

function ASAToFloat (asaInfo: ASAInfo, amount: number) {
return amount / (10 ** asaInfo.params.decimals)
}

const txns = await contracts.fixedBid.updatePrice({
appId: 210217503,
unitPrice: ASAToBaseInt(asa, set.price)
})
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dartroom/contracts",
"version": "0.2.3-alpha",
"version": "0.2.5-alpha",
"description": "A package that includes all Dartroom's smart contracts written in TEAL, with complete interfaces in TypeScript to interact with them.",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class Contracts<E extends boolean, B extends 'Uint8Array' | 'Base64', S e
/**
* Create a new Transaction formatter instance. With the formatter you can create a new transaction array in the same format as the contract package returns.
*/
newTxnFormater () {
newTxnFormatter () {
return new TxnFormatter<E, B, S, A>(this)
}
}
13 changes: 11 additions & 2 deletions src/functions/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ function hexToBytes (hex: string) {
return bytes;
}

function hashAbiMethod (method: string) {
return new Uint8Array(hexToBytes(sha512_256(method).substring(0, 8)))
function hashAbiMethod (method: string): Uint8Array
function hashAbiMethod (method: string, encoding: BufferEncoding): string
function hashAbiMethod (method: string, encoding?: BufferEncoding) {

const bytes = hexToBytes(sha512_256(method).substring(0, 8))

if (!encoding) {
return new Uint8Array(bytes)
}

return Buffer.from(bytes).toString(encoding)
}

export { hashAbiMethod }
13 changes: 7 additions & 6 deletions src/functions/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import algosdk from "algosdk";
interface StateValueObject {
key: string
value: {
bytes: string
type: number
uint: number
bytes?: string
type?: number
uint?: number
action?: number
}
}

Expand All @@ -22,7 +23,7 @@ function encodeStateAddress (address: string): string {
export function getGlobalByte(state: GlobalState, key: string): string {
const stateValue = findState(state, key)

if (stateValue) {
if (stateValue && stateValue.value.bytes) {
return stateValue.value.bytes
} else {
return ""
Expand All @@ -32,7 +33,7 @@ export function getGlobalByte(state: GlobalState, key: string): string {
export function getGlobalUint(state: GlobalState, key: string): number {
const stateValue = findState(state, key)

if (stateValue) {
if (stateValue && stateValue.value.uint) {
return stateValue.value.uint
} else {
return -1
Expand All @@ -42,7 +43,7 @@ export function getGlobalUint(state: GlobalState, key: string): number {
export function getGlobalAddress(state: GlobalState, key: string): string {
const stateValue = findState(state, key)

if (stateValue) {
if (stateValue && stateValue.value.bytes) {
return encodeStateAddress(stateValue.value.bytes)
} else {
return ""
Expand Down
8 changes: 7 additions & 1 deletion src/interface/fixedBid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Sell a number of NFTs for a fixed price in either Algorand or ASA currencies (AC
- [Buy](#buy)
- [Destroy](#destory)
- [Get Global State](#get-global-state)
- [Get Status](#get-status)
- [Get History](#get-history)

# Deploy

Expand All @@ -26,4 +28,8 @@ Sell a number of NFTs for a fixed price in either Algorand or ASA currencies (AC

# Destroy

# Get Global State
# Get Global State

# Get status

# Get History
Loading

0 comments on commit 0615c7e

Please sign in to comment.