Skip to content
Open
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
9 changes: 8 additions & 1 deletion scripts/crossChainPayment/collectAndProcessTransferEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,14 @@ async function interactWithContract(nftMinter: NFTMinterInstance, proof: any) {
data: decodedResponse,
});
console.log("Transaction:", transaction.tx, "\n");
console.log("Token transfer:", await nftMinter.tokenTransfers(0), "\n");
const transfers = await nftMinter.getTokenTransfers();
if (transfers.length > 0) {
console.log("Token transfer:", await nftMinter.tokenTransfers(0), "\n");
} else {
console.log(
"No matching token transfers found in the transaction (expected for non-USDC Sepolia transactions).\n"
);
}
}

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion scripts/customFeeds/InflationDataVerification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const INDICATOR_CODE = "FP.CPI.TOTL.ZG"; // World Bank Indicator for CPI, annual
// --- World Bank API Request Data ---
const apiUrl = `https://api.worldbank.org/v2/country/US/indicator/${INDICATOR_CODE}`;
const stringifiedQueryParams = JSON.stringify({ format: "json", date: targetYear });
const postprocessJq = `{inflationRate: (.[1][0].value | tonumber * 100 | floor), observationYear: (.[1][0].date | tonumber)}`;
const postprocessJq = `{inflationRate: (.[1][0].value * 100), observationYear: ${targetYear}}`;
const abiSig = `{"components": [{"internalType": "uint256","name": "inflationRate","type": "uint256"}, {"internalType": "uint256","name": "observationYear","type": "uint256"}],"internalType": "struct InflationData","name": "inflationData","type": "tuple"}`;

// --- FDC Configuration ---
Expand Down
2 changes: 1 addition & 1 deletion scripts/customFeeds/MetalPriceVerification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (!supportedMetals.includes(metalSymbol)) {

// --- Swissquote Metal Price Request Data ---
const fullApiUrl = `https://forex-data-feed.swissquote.com/public-quotes/bboquotes/instrument/${metalSymbol}/USD`;
const postprocessJq = `{price: (.[0].spreadProfilePrices[0].ask * 10000 | floor)}`; // Multiply to get 4 decimal places of precision
const postprocessJq = `{price: (.[0].spreadProfilePrices[0].ask * 10000)}`; // Multiply to get 4 decimal places of precision
const abiSig = `{"components": [{"internalType": "uint256","name": "price","type": "uint256"}],"internalType": "struct MetalPriceData","name": "priceData","type": "tuple"}`;

// --- FDC Configuration ---
Expand Down
2 changes: 1 addition & 1 deletion scripts/customFeeds/PriceVerification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const month = String(dateToFetch.getMonth() + 1).padStart(2, "0");
const year = dateToFetch.getFullYear();
const dateString = `${day}-${month}-${year}`;
const fullApiUrl = `https://api.coingecko.com/api/v3/coins/${coinGeckoId}/history`;
const postprocessJq = `{price: (.market_data.current_price.usd * ${10 ** priceDecimals} | floor)}`;
const postprocessJq = `{price: (.market_data.current_price.usd * ${10 ** priceDecimals})}`;
const abiSig = `{"components": [{"internalType": "uint256","name": "price","type": "uint256"}],"internalType": "struct PriceData","name": "priceData","type": "tuple"}`;
const stringifiedQueryParams = JSON.stringify({
date: dateString,
Expand Down
7 changes: 7 additions & 0 deletions scripts/fassets/config/mintingConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Auto-generated by reserveCollateral.ts — do not edit manually
export const collateralReservationId = 0;
export const paymentAddress = "";
export const paymentReference = "";
export const amountXRP = "";
export const xrpTransactionHash = "";
export const fdcRoundId = 0;
90 changes: 49 additions & 41 deletions scripts/fassets/executeMinting.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
import { web3 } from "hardhat";
import { getAssetManagerFXRP } from "../utils/getters";
import { prepareAttestationRequestBase } from "../utils/fdc";
import {
prepareAttestationRequestBase,
submitAttestationRequest,
retrieveDataAndProofBaseWithRetry,
} from "../utils/fdc";
import { IAssetManagerInstance } from "../../typechain-types";
import { logEvents } from "../../scripts/utils/core";
import { collateralReservationId, xrpTransactionHash } from "./config/mintingConfig";

// yarn hardhat run scripts/fassets/executeMinting.ts --network coston2

// Environment variables
const { COSTON2_DA_LAYER_URL, VERIFIER_URL_TESTNET, VERIFIER_API_KEY_TESTNET } = process.env;

// Collateral reservation ID
const COLLATERAL_RESERVATION_ID = 10255417;

// FDC round id to get the proof for
const TARGET_ROUND_ID = 1053806;

// FDC request data
const attestationTypeBase = "Payment";
const sourceIdBase = "testXRP";
const verifierUrlBase = VERIFIER_URL_TESTNET;
const urlTypeBase = "xrp";

const transactionId = "EC0FC5F40FBE6AEAD31138898C71687B2902E462FD1BFEF3FB443BE5E2C018F9";
const inUtxo = "0";
const utxo = "0";

const AssetManager = artifacts.require("IAssetManager");

// Prepare FDC request
async function prepareFdcRequest(transactionId: string, inUtxo: string, utxo: string) {
async function prepareFdcRequest(transactionId: string) {
const requestBody = {
transactionId: transactionId,
inUtxo: inUtxo,
utxo: utxo,
inUtxo: "0",
utxo: "0",
};

const url = `${verifierUrlBase}/verifier/${urlTypeBase}/Payment/prepareRequest`;
Expand All @@ -45,53 +41,65 @@ async function prepareFdcRequest(transactionId: string, inUtxo: string, utxo: st
);
}

// Get proof from FDC
async function getProof(roundId: number) {
const request = await prepareFdcRequest(transactionId, inUtxo, utxo);
const proofAndData = await fetch(`${COSTON2_DA_LAYER_URL}/api/v0/fdc/get-proof-round-id-bytes`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": VERIFIER_API_KEY_TESTNET,
},
body: JSON.stringify({
votingRoundId: roundId,
requestBytes: request.abiEncodedRequest,
}),
});

return await proofAndData.json();
async function retrieveDataAndProof(abiEncodedRequest: string, roundId: number) {
const url = `${COSTON2_DA_LAYER_URL}/api/v1/fdc/proof-by-request-round-raw`;
console.log("Url:", url, "\n");
return await retrieveDataAndProofBaseWithRetry(url, abiEncodedRequest, roundId);
}

function parseEvents(receipt: any) {
console.log("\nParsing events...", receipt.rawLogs);

logEvents(receipt.rawLogs, "RedemptionTicketCreated", AssetManager.abi);

logEvents(receipt.rawLogs, "MintingExecuted", AssetManager.abi);
}

async function main() {
const proof = await getProof(TARGET_ROUND_ID);
if (!xrpTransactionHash) {
throw new Error("No XRP transaction hash found in config. Run xrpPayment.ts first.");
}

// FAssets FXRP asset manager on Songbird Testnet Coston2 network
console.log("Using collateral reservation ID:", collateralReservationId);
console.log("Using XRP transaction hash:", xrpTransactionHash);

// Prepare the attestation request
const data = await prepareFdcRequest(xrpTransactionHash);
console.log("Data:", data, "\n");

const abiEncodedRequest = data.abiEncodedRequest;

// Submit attestation request and get the round ID
const roundId = await submitAttestationRequest(abiEncodedRequest);

// Wait for proof
const proof = await retrieveDataAndProof(abiEncodedRequest, roundId);

// FAssets FXRP asset manager on Coston2
const assetManager: IAssetManagerInstance = await getAssetManagerFXRP();

// Decode the response_hex into the Payment.Response struct
// The v1 DA layer API returns response_hex but not the decoded response
const IPaymentVerification = await artifacts.require("IPaymentVerification");
const responseType = IPaymentVerification._json.abi[0].inputs[0].components[1];
const decodedResponse = web3.eth.abi.decodeParameter(responseType, proof.response_hex);
console.log("Decoded response:", decodedResponse, "\n");

// Execute minting
const tx = await assetManager.executeMinting(
{
merkleProof: proof.proof,
data: proof.response,
data: decodedResponse,
},
COLLATERAL_RESERVATION_ID
collateralReservationId
);
console.log("Transaction successful:", tx);
console.log("Minting executed successfully:", tx.tx);

// Parse execute minting log events
parseEvents(tx.receipt);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
2 changes: 1 addition & 1 deletion scripts/fassets/mintingCap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getAssetManagerFXRP } from "../utils/getters";

// Run with: yarn hardhat run scripts/fassets/mintingCap.ts --network coston2

const IERC20 = artifacts.require("ERC20");
const IERC20 = artifacts.require("@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20");

// Agent status constants
const AGENT_STATUS_NORMAL = 0;
Expand Down
5 changes: 2 additions & 3 deletions scripts/fassets/redeem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ async function main() {
await printRedemptionRequestInfo(fAssetsRedeem, redemptionRequestedEvents);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
void main().then(() => {
process.exit(0);
});
20 changes: 17 additions & 3 deletions scripts/fassets/reserveCollateral.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import fs from "fs";
import path from "path";
import { getAssetManagerFXRP } from "../utils/getters";
import { IAssetManagerInstance } from "../../typechain-types";
import { logEvents } from "../../scripts/utils/core";
Expand Down Expand Up @@ -117,9 +119,21 @@ async function main() {
const totalUBA = valueUBA + feeUBA;
const totalXRP = Number(totalUBA) / 10 ** decimals;
console.log(`You need to pay ${totalXRP} XRP`);

// Write config for the next scripts in the minting pipeline
const configContent = `// Auto-generated by reserveCollateral.ts — do not edit manually
export const collateralReservationId = ${collateralReservedEvent.collateralReservationId};
export const paymentAddress = "${collateralReservedEvent.paymentAddress}";
export const paymentReference = "${collateralReservedEvent.paymentReference}";
export const amountXRP = "${totalXRP}";
export const xrpTransactionHash = "";
export const fdcRoundId = 0;
`;
const configPath = path.join(__dirname, "config", "mintingConfig.ts");
fs.writeFileSync(configPath, configContent);
console.log(`Minting config written to ${configPath}`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
void main().then(() => {
process.exit(0);
});
49 changes: 36 additions & 13 deletions scripts/fassets/xrpPayment.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,69 @@
// install xrpl package:
// npm install xrpl
import fs from "fs";
import path from "path";
import { Client, Wallet, xrpToDrops, TxResponse, Payment } from "xrpl";
import { collateralReservationId, paymentAddress, paymentReference, amountXRP } from "./config/mintingConfig";

// yarn hardhat run scripts/fassets/xrpPayment.ts

const AGENT_ADDRESS = "r4KgCNzn9ZuNjpf17DEHZnyyiqpuj599Wm"; // Agent underlying chain address
const AMOUNT_XRP = "10.025"; // XRP amount to send
const PAYMENT_REFERENCE = "4642505266410001000000000000000000000000000000000000000000f655fb";
const { XRPL_SECRET } = process.env;

async function send20XrpWithReference() {
async function sendXrpWithReference() {
const client = new Client("wss://s.altnet.rippletest.net:51233"); // Testnet
await client.connect();

// XRP Ledger Testnet seed
const wallet: Wallet = Wallet.fromSeed("s000000000000000000000000000000"); // Sender wallet seed
// Use provided XRPL secret or fall back to generating a funded wallet
let wallet: Wallet;
if (XRPL_SECRET) {
wallet = Wallet.fromSeed(XRPL_SECRET);
} else {
console.log("No XRPL_SECRET provided, funding a new testnet wallet...");
const fundResult = await client.fundWallet();
wallet = fundResult.wallet;
console.log("Funded wallet address:", wallet.address);
}

console.log("Sending XRP payment:");
console.log(" To:", paymentAddress);
console.log(" Amount:", amountXRP, "XRP");
console.log(" Reference:", paymentReference);
console.log(" Collateral Reservation ID:", collateralReservationId);

const payment: Payment = {
TransactionType: "Payment",
Account: wallet.address,
Destination: AGENT_ADDRESS,
Amount: xrpToDrops(AMOUNT_XRP),
Destination: paymentAddress,
Amount: xrpToDrops(amountXRP),
// Payment reference
Memos: [
{
Memo: {
MemoData: PAYMENT_REFERENCE,
MemoData: paymentReference.startsWith("0x") ? paymentReference.slice(2) : paymentReference,
},
},
],
};

console.log(payment);

const prepared = await client.autofill(payment);
const signed = wallet.sign(prepared);
const result: TxResponse = await client.submitAndWait(signed.tx_blob);

console.log("Transaction hash:", signed.hash);
console.log("Explorer: https://testnet.xrpl.org/transactions/" + signed.hash);
console.log("Result:", result);
console.log("Result:", result.result.meta);

// Update the minting config with the transaction hash
const configPath = path.join(__dirname, "config", "mintingConfig.ts");
let config = fs.readFileSync(configPath, "utf-8");
config = config.replace(
/export const xrpTransactionHash = ".*";/,
`export const xrpTransactionHash = "${signed.hash}";`
);
fs.writeFileSync(configPath, config);
console.log(`Updated minting config with transaction hash: ${signed.hash}`);

await client.disconnect();
}

send20XrpWithReference().catch(console.error);
sendXrpWithReference().catch(console.error);
4 changes: 2 additions & 2 deletions scripts/fdcExample/BalanceDecreasingTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const { VERIFIER_URL_TESTNET, VERIFIER_API_KEY_TESTNET, COSTON2_DA_LAYER_URL } =
// yarn hardhat run scripts/fdcExample/BalanceDecreasingTransaction.ts --network coston2

// Request data
const transactionId = "CDBAF39222C04C9FD3AE0FA54B144FFA6D580ABDB0A888EA5B1CFDDEE282156F";
const sourceAddress = "rGpGUdqUAVkNVr4Hfkvay7ffB7vjoA31uT";
const transactionId = "576FFB91E76FC28E38D07F8FF513EE76EBFE813D8181E1E8D025E256C26963E0";
const sourceAddress = "rJjHYTCPpNA3qAM8ZpCDtip3a8xg7B8PFo";

// Configuration constants
const attestationTypeBase = "BalanceDecreasingTransaction";
Expand Down
17 changes: 15 additions & 2 deletions scripts/fdcExample/ConfirmedBlockHeightExists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ const { VERIFIER_URL_TESTNET, VERIFIER_API_KEY_TESTNET, COSTON2_DA_LAYER_URL } =

// yarn hardhat run scripts/fdcExample/ConfirmedBlockHeightExists.ts --network coston2

// Request data
const blockNumber = "5004226";
// Request data - fetched dynamically to avoid stale block numbers
const queryWindow = "1"; // in seconds

// Configuration constants
Expand Down Expand Up @@ -43,7 +42,21 @@ async function deployAndVerifyContract() {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function interactWithContract() {}

async function getRecentXrpLedgerIndex(): Promise<string> {
const response = await fetch("https://s.altnet.rippletest.net:51234/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ method: "ledger", params: [{ ledger_index: "validated" }] }),
});
const json = await response.json();
// Use a block ~10 behind the latest to ensure it's well confirmed
const ledgerIndex = json.result.ledger_index - 10;
console.log("Using XRP ledger index:", ledgerIndex, "\n");
return ledgerIndex.toString();
}

async function main() {
const blockNumber = await getRecentXrpLedgerIndex();
const data = await prepareAttestationRequest(blockNumber, queryWindow);
console.log("Data:", data, "\n");

Expand Down
Loading