Skip to content

Commit

Permalink
Merge pull request #12 from 00labs/allow-separate-metadata-uploads
Browse files Browse the repository at this point in the history
Split out metadata uploading from ReceivableService.createReceivableWithMetadata
  • Loading branch information
mliu authored Aug 17, 2023
2 parents f10bf2d + f80b215 commit 761a33b
Show file tree
Hide file tree
Showing 25 changed files with 646 additions and 364 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/beta-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ jobs:
- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Authenticate with NPM
run: echo "//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}" > .npmrc

- name: Configure Git User
run: |
git config --global user.email "ci@huma.finance"
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ on:
jobs:
ci:
runs-on: ubuntu-latest
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

steps:
- name: Checkout
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/production-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ jobs:
- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Authenticate with NPM
run: echo "//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}" > .npmrc

- name: Configure Git User
run: |
git config --global user.email "ci@huma.finance"
Expand Down
Empty file modified .husky/post-commit
100755 → 100644
Empty file.
Empty file modified .husky/pre-commit
100755 → 100644
Empty file.
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

3 changes: 2 additions & 1 deletion packages/examples/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
REACT_APP_INFURA_API_KEY=
REACT_APP_ALCHEMY_API_KEY=
TEST_PRIVATE_KEY=
REACT_APP_ALCHEMY_API_KEY_2=
TEST_PRIVATE_KEY=
3 changes: 2 additions & 1 deletion packages/examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"private": true,
"main": "src/mintReceivable.js",
"scripts": {
"startMint": "ts-node src/mintReceivable.ts",
"startCreate": "ts-node src/createReceivable.ts",
"startCreateAlt": "ts-node src/createReceivableAlternateNetwork.ts",
"startPay": "ts-node src/payReceivable.ts",
"startPayReference": "ts-node src/payReceivableWithReferenceId.ts",
"startDrawdownPayback": "ts-node src/drawdownAndPaybackToPool.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async function main() {
1000, // receivableAmount
1684517656, // maturityDate
JSON.parse('{"test": "test"}'), // metadata
"12345", // internalId
"1234235", // referenceId
[] // extraTags
);
const txResponse = await tx.wait();
Expand Down
85 changes: 85 additions & 0 deletions packages/examples/src/createReceivableAlternateNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Wallet, ethers } from "ethers";
import {
ARWeaveService,
ReceivableService,
getBundlrNetworkConfig,
} from "@huma-finance/sdk";
import { ChainEnum, POOL_NAME, POOL_TYPE } from "@huma-finance/shared";
require("dotenv").config();

/*
* We use Bundlr to upload metadata to ARWeave, which allows for users to pay in popular currencies like MATIC or ETH
* instead of AR tokens. Certain networks that are still unsupported by Bundlr (e.g. Celo) can still use Bundlr to
* fund the ARWeave uploads by simply paying on a supported network.
*
* This snippet shows how to use a wallet with MATIC to fund the ARWeave uploads, and then that same wallet on
* a separate network to upload a RealWorldReceivable with the resulting metadata URI.
*/
async function main() {
const TEST_PRIVATE_KEY = process.env.TEST_PRIVATE_KEY;

// We'll be using a mumbai wallet funded with MATIC to pay for the ARWeave uploads
const mumbaiProvider = new ethers.providers.JsonRpcProvider(
`https://polygon-mumbai.g.alchemy.com/v2/${process.env.REACT_APP_ALCHEMY_API_KEY}`,
{
name: "Mumbai",
chainId: ChainEnum.Mumbai,
}
);
const walletOnSupportedBundlrNetwork = new Wallet(
TEST_PRIVATE_KEY,
mumbaiProvider
);

// On a separate network which may not be supported by Bundlr, we'll use our wallet
// to create a RealWorldReceivable with the metadata URI from ARWeave
const rwrProvider = new ethers.providers.JsonRpcProvider(
`https://eth-goerli.g.alchemy.com/v2/${process.env.REACT_APP_ALCHEMY_API_KEY_2}`,
{
name: "Goerli",
chainId: ChainEnum.Goerli,
}
);
const walletOnRWRNetwork = new Wallet(TEST_PRIVATE_KEY, rwrProvider);

console.log(
`Using ${walletOnSupportedBundlrNetwork.address} to upload metadata`
);
console.log(
`Using ${walletOnRWRNetwork.address} to create RealWorldReceivable`
);

// Prefund Bundlr with MATIC
const fundResponse = await ARWeaveService.prefundBundlr(
getBundlrNetworkConfig(ChainEnum.Mumbai),
TEST_PRIVATE_KEY,
0.05 // Fund with 0.05 matic
);
console.log(fundResponse);

const uri = await ReceivableService.uploadOrFetchMetadataURI(
walletOnSupportedBundlrNetwork,
TEST_PRIVATE_KEY,
ChainEnum.Mumbai,
POOL_NAME.HumaCreditLine,
POOL_TYPE.CreditLine,
JSON.parse('{"test": "test"}'), // metadata
"1234567", // referenceId
[{ name: "indexedIdentifier", value: "exampleValue" }] // extraTags
);

// Mint a receivable with metadata uploaded to ARWeave
const tx = await ReceivableService.createReceivable(
walletOnRWRNetwork,
POOL_NAME.HumaCreditLine,
POOL_TYPE.CreditLine,
840, // currencyCode for USD
1000, // receivableAmount
1684517656, // maturityDate
uri // metadataURI
);
const txResponse = await tx.wait();
console.log(`Success. Tx hash: ${txResponse.transactionHash}`);
}

main();
54 changes: 45 additions & 9 deletions packages/huma-sdk/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ Note that this does not approve a creditline in Huma's pools and an approve call
* [.storeData(config, signerOrPrivateKey, data, tags, [lazyFund])](#ARWeaveService.storeData) ⇒ <code>Promise.&lt;UploadResponse&gt;</code>
* [.queryForMetadata(chainId, sender, referenceId)](#ARWeaveService.queryForMetadata) ⇒ <code>Promise.&lt;any&gt;</code>
* [.fetchMetadataFromUrl(url)](#ARWeaveService.fetchMetadataFromUrl) ⇒ <code>Promise.&lt;JSON&gt;</code>
* [.getURIFromARWeaveId(arweaveId)](#ARWeaveService.getURIFromARWeaveId) ⇒ <code>string</code>
* [.BundlrConfig](#ARWeaveService.BundlrConfig) : <code>Object</code>

<a name="ARWeaveService.getBundlrNetworkConfig"></a>
Expand Down Expand Up @@ -229,6 +230,18 @@ If you want to upload 5 Matic to the Bundlr node, pass in an amount of 5.</p>
| --- | --- | --- |
| url | <code>string</code> | <p>The ARWeave metadata URL to query.</p> |

<a name="ARWeaveService.getURIFromARWeaveId"></a>

### ARWeaveService.getURIFromARWeaveId(arweaveId) ⇒ <code>string</code>
<p>Helper method to get an ARWeave URI from an ARWeave ID.</p>

**Kind**: static method of [<code>ARWeaveService</code>](#ARWeaveService)
**Returns**: <code>string</code> - <p>The ARWeave URI.</p>

| Param | Type | Description |
| --- | --- | --- |
| arweaveId | <code>string</code> | <p>The ARWeave metadata ID.</p> |

<a name="ARWeaveService.BundlrConfig"></a>

### ARWeaveService.BundlrConfig : <code>Object</code>
Expand Down Expand Up @@ -275,17 +288,18 @@ in Huma's pools that can be drawn down by the borrower.</p>
**Kind**: global namespace

* [ReceivableService](#ReceivableService) : <code>object</code>
* [.getTokenIdByARWeaveId(signer, arweaveId)](#ReceivableService.getTokenIdByARWeaveId) ⇒ <code>Promise.&lt;(string\|null\|undefined)&gt;</code>
* [.getTokenIdByURI(signer, arweaveId)](#ReceivableService.getTokenIdByURI) ⇒ <code>Promise.&lt;(string\|null\|undefined)&gt;</code>
* [.declareReceivablePaymentByReferenceId(signer, referenceId, paymentAmount, [gasOpts])](#ReceivableService.declareReceivablePaymentByReferenceId) ⇒ <code>Promise.&lt;TransactionResponse&gt;</code>
* [.declareReceivablePaymentByTokenId(signer, receivableTokenId, paymentAmount, [gasOpts])](#ReceivableService.declareReceivablePaymentByTokenId) ⇒ <code>Promise.&lt;TransactionResponse&gt;</code>
* [.createReceivable(signer, poolName, poolType, currencyCode, receivableAmount, maturityDate, uri, [gasOpts])](#ReceivableService.createReceivable) ⇒ <code>Promise.&lt;TransactionResponse&gt;</code>
* [.createReceivable(signer, poolName, poolType, currencyCode, receivableAmount, maturityDate, uri, [gasOpts])](#ReceivableService.createReceivable) ⇒ <code>Promise.&lt;(TransactionResponse\|null)&gt;</code>
* [.uploadOrFetchMetadataURI(signerOrProvider, privateKey, chainId, poolName, poolType, metadata, referenceId, extraTags, [lazyFund])](#ReceivableService.uploadOrFetchMetadataURI) ⇒ <code>Promise.&lt;string&gt;</code>
* [.createReceivableWithMetadata(signerOrProvider, privateKey, chainId, poolName, poolType, currencyCode, receivableAmount, maturityDate, metadata, referenceId, extraTags, [lazyFund], [gasOpts])](#ReceivableService.createReceivableWithMetadata) ⇒ <code>Promise.&lt;TransactionResponse&gt;</code>
* [.loadReceivablesOfOwnerWithMetadata(signerOrProvider, owner, poolName, poolType)](#ReceivableService.loadReceivablesOfOwnerWithMetadata) ⇒ <code>Promise.&lt;Array.&lt;RealWorldReceivableInfo&gt;&gt;</code>

<a name="ReceivableService.getTokenIdByARWeaveId"></a>
<a name="ReceivableService.getTokenIdByURI"></a>

### ReceivableService.getTokenIdByARWeaveId(signer, arweaveId) ⇒ <code>Promise.&lt;(string\|null\|undefined)&gt;</code>
<p>Declares a payment on a RealWorldReceivable given a reference Id of the receivable, which was used as an index for ARWeave data.</p>
### ReceivableService.getTokenIdByURI(signer, arweaveId) ⇒ <code>Promise.&lt;(string\|null\|undefined)&gt;</code>
<p>Fetches the tokenId of a RealWorldReceivable, or null if it doesn't exist, given a metadata URI</p>

**Kind**: static method of [<code>ReceivableService</code>](#ReceivableService)
**Returns**: <code>Promise.&lt;(string\|null\|undefined)&gt;</code> - <ul>
Expand Down Expand Up @@ -339,17 +353,17 @@ in Huma's pools that can be drawn down by the borrower.</p>

<a name="ReceivableService.createReceivable"></a>

### ReceivableService.createReceivable(signer, poolName, poolType, currencyCode, receivableAmount, maturityDate, uri, [gasOpts]) ⇒ <code>Promise.&lt;TransactionResponse&gt;</code>
### ReceivableService.createReceivable(signer, poolName, poolType, currencyCode, receivableAmount, maturityDate, uri, [gasOpts]) ⇒ <code>Promise.&lt;(TransactionResponse\|null)&gt;</code>
<p>Creates a new RealWorldReceivable token on the given chain of the signer</p>

**Kind**: static method of [<code>ReceivableService</code>](#ReceivableService)
**Returns**: <code>Promise.&lt;TransactionResponse&gt;</code> - <ul>
**Returns**: <code>Promise.&lt;(TransactionResponse\|null)&gt;</code> - <ul>
<li>A Promise of the transaction response.</li>
</ul>
**Throws**:

- <code>Error</code> <ul>
<li>Throws an error if the RealWorldReceivable contract is not available on the network.</li>
<li>Throws an error if the RealWorldReceivable contract is not available on the network, or if a token already exists with the same metadata URI.</li>
</ul>


Expand All @@ -364,6 +378,28 @@ in Huma's pools that can be drawn down by the borrower.</p>
| uri | <code>string</code> | <p>The URI of the receivable token metadata.</p> |
| [gasOpts] | <code>Overrides</code> | <p>The gas options to use for the transaction.</p> |

<a name="ReceivableService.uploadOrFetchMetadataURI"></a>

### ReceivableService.uploadOrFetchMetadataURI(signerOrProvider, privateKey, chainId, poolName, poolType, metadata, referenceId, extraTags, [lazyFund]) ⇒ <code>Promise.&lt;string&gt;</code>
<p>Uploads metadata onto ARWeave (or fetches the existing metadata with the same reference Id) and returns the ARWeave URL</p>

**Kind**: static method of [<code>ReceivableService</code>](#ReceivableService)
**Returns**: <code>Promise.&lt;string&gt;</code> - <ul>
<li>The ARWeave metadata URI.</li>
</ul>

| Param | Type | Default | Description |
| --- | --- | --- | --- |
| signerOrProvider | <code>Web3Provider</code> \| <code>ethers.Signer</code> | | <p>If calling this function from a browser, this function expects a Web3Provider. If calling this function from a server, this function expects an ethers Signer. Note that privateKey only needs to be included from server calls.</p> |
| privateKey | <code>string</code> \| <code>null</code> | | <p>Private key of the wallet used to upload metadata to ARWeave. Only required if calling this function from a server.</p> |
| chainId | <code>number</code> | | <p>The chain ID to mint the receivable token on and pay ARWeave funds from.</p> |
| poolName | <code>POOL\_NAME</code> | | <p>The pool name. Used to lookup the pool address to pay to.</p> |
| poolType | <code>POOL\_TYPE</code> | | <p>The pool type. Used to lookup the pool address to pay to.</p> |
| metadata | <code>Record.&lt;string, any&gt;</code> | | <p>The metadata in JSON format. This will be uploaded onto ARWeave</p> |
| referenceId | <code>string</code> | | <p>An internal identifier value added as a tag on ARWeave, for easily querying the metadata later.</p> |
| extraTags | <code>Array.&lt;{name: string, value: string}&gt;</code> | | <p>Any extraTags you'd like to tag your metadata with. Note that metadata on ARWeave is indexed by these tags, so make sure to include any tags that you'd like to be able to query by.</p> |
| [lazyFund] | <code>boolean</code> | <code>true</code> | <p>Whether to lazy fund the ARWeave uploads. If true, the ARWeave uploads will be paid for by the metadata uploader immediately before uploading. If false, the ARWeave node must be pre-funded before calling this function.</p> |

<a name="ReceivableService.createReceivableWithMetadata"></a>

### ReceivableService.createReceivableWithMetadata(signerOrProvider, privateKey, chainId, poolName, poolType, currencyCode, receivableAmount, maturityDate, metadata, referenceId, extraTags, [lazyFund], [gasOpts]) ⇒ <code>Promise.&lt;TransactionResponse&gt;</code>
Expand All @@ -385,7 +421,7 @@ in Huma's pools that can be drawn down by the borrower.</p>
| receivableAmount | <code>number</code> | | <p>The receivable amount.</p> |
| maturityDate | <code>number</code> | | <p>The maturity date of the receivable, in UNIX timestamp format.</p> |
| metadata | <code>Record.&lt;string, any&gt;</code> | | <p>The metadata in JSON format. This will be uploaded onto ARWeave</p> |
| referenceId | <code>number</code> | | <p>An internal identifier value added as a tag on ARWeave, for easily querying the metadata later.</p> |
| referenceId | <code>string</code> | | <p>An internal identifier value added as a tag on ARWeave, for easily querying the metadata later.</p> |
| extraTags | <code>Array.&lt;{name: string, value: string}&gt;</code> | | <p>Any extraTags you'd like to tag your metadata with. Note that metadata on ARWeave is indexed by these tags, so make sure to include any tags that you'd like to be able to query by.</p> |
| [lazyFund] | <code>boolean</code> | <code>true</code> | <p>Whether to lazy fund the ARWeave uploads. If true, the ARWeave uploads will be paid for by the metadata uploader immediately before uploading. If false, the ARWeave node must be pre-funded before calling this function.</p> |
| [gasOpts] | <code>Overrides</code> | | <p>Optional gas overrides for the transaction.</p> |
Expand Down
2 changes: 1 addition & 1 deletion packages/huma-sdk/coverage/badge-functions.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/huma-sdk/coverage/badge-lines.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions packages/huma-sdk/src/services/ARWeaveService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,19 @@ async function fetchMetadataFromUrl(url: string): Promise<JSON | null> {
}
}

/**
* Helper method to get an ARWeave URI from an ARWeave ID.
*
* @async
* @function
* @memberof ARWeaveService
* @param {string} arweaveId - The ARWeave metadata ID.
* @returns {string} The ARWeave URI.
*/
function getURIFromARWeaveId(arweaveId: string): string {
return `https://arweave.net/${arweaveId}`
}

/**
* An object that contains functions to interact with Huma-related data stored on ARWeave
* @namespace ARWeaveService
Expand All @@ -248,4 +261,5 @@ export const ARWeaveService = {
getBundlrNetworkConfig,
getBundlrInstance,
fetchMetadataFromUrl,
getURIFromARWeaveId,
}
Loading

0 comments on commit 761a33b

Please sign in to comment.