Skip to content

Commit

Permalink
feat: matic support (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
arielmelendez authored Sep 9, 2024
2 parents 52350b5 + 3844c0f commit a807e4f
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ export const ethereumGatewayUrl = new URL(
process.env.ETHEREUM_GATEWAY || "https://cloudflare-eth.com/"
);

export const maticGatewayUrl = new URL(
process.env.MATIC_GATEWAY || "https://polygon-mainnet.infura.io/"
);

export const solanaGatewayUrl = new URL(
process.env.SOLANA_GATEWAY || "https://api.mainnet-beta.solana.com/"
);
Expand Down
1 change: 1 addition & 0 deletions src/database/dbTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const userAddressTypes = [
"solana",
"ethereum",
"kyve",
"matic"
] as const;
export type UserAddressType = (typeof userAddressTypes)[number];

Expand Down
1 change: 1 addition & 0 deletions src/gateway/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const supportedPaymentTokens = [
"ethereum",
"solana",
"kyve",
"matic"
] as const;
export type TokenType = (typeof supportedPaymentTokens)[number];
export function isSupportedPaymentToken(token: string): token is TokenType {
Expand Down
1 change: 1 addition & 0 deletions src/gateway/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export * from "./kyve";
export * from "./solana";
export * from "./ethereum";
export * from "./arweave";
export * from './matic';
93 changes: 93 additions & 0 deletions src/gateway/matic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import BigNumber from "bignumber.js";
import { ethers } from "ethers";

import { maticGatewayUrl } from "../constants";
import { PaymentTransactionNotFound } from "../database/errors";
import logger from "../logger";
import { TransactionId } from "../types";
import {
Gateway,
GatewayParams,
TransactionInfo,
TransactionStatus,
} from "./gateway";

type MaticGatewayParams = GatewayParams;

export class MaticGateway extends Gateway {
public endpoint: URL;
private provider: ethers.JsonRpcProvider;

constructor({
endpoint = maticGatewayUrl,
paymentTxPollingWaitTimeMs,
pendingTxMaxAttempts,
minConfirmations = +(process.env.MATIC_MIN_CONFIRMATIONS || 5),
}: MaticGatewayParams = {}) {
super({
paymentTxPollingWaitTimeMs,
pendingTxMaxAttempts,
minConfirmations,
});
this.endpoint = endpoint;
this.provider = new ethers.JsonRpcProvider(endpoint.toString());
}

public async getTransactionStatus(
transactionId: TransactionId
): Promise<TransactionStatus> {
logger.debug("Getting transaction status...", { transactionId });
const statusResponse = await this.provider.getTransactionReceipt(
transactionId
);
if (statusResponse === null) {
logger.debug("Transaction not found...", { transactionId });
return { status: "not found" };
}

if ((await statusResponse.confirmations()) >= this.minConfirmations) {
return {
status: "confirmed",
blockHeight: statusResponse.blockNumber,
};
}

return { status: "pending" };
}

public async getTransaction(
transactionId: TransactionId
): Promise<TransactionInfo> {
return this.pollGatewayForTx(async () => {
logger.debug("Getting transaction...", { transactionId });
const txResponse = await this.provider.getTransaction(transactionId);
if (txResponse === null) {
throw new PaymentTransactionNotFound(transactionId);
}

const tx = {
transactionQuantity: BigNumber(txResponse.value.toString()),
transactionSenderAddress: txResponse.from,
transactionRecipientAddress: txResponse.to ?? "",
};

return tx;
}, transactionId);
}
}
2 changes: 2 additions & 0 deletions src/jobs/creditPendingTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
EthereumGateway,
KyveGateway,
SolanaGateway,
MaticGateway,
} from "../gateway";
import globalLogger from "../logger";

Expand All @@ -39,6 +40,7 @@ export async function creditPendingTransactionsHandler({
ethereum: new EthereumGateway(),
solana: new SolanaGateway(),
kyve: new KyveGateway(),
matic: new MaticGateway()
},
paymentDatabase = new PostgresDatabase(),
logger = globalLogger.child({ job: "credit-pending-transactions-job" }),
Expand Down
2 changes: 2 additions & 0 deletions src/pricing/oracles/tokenToFiatOracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const coinGeckoTokenNames = [
"ethereum",
"solana",
"kyve-network",
"matic-network"
] as const;

type CoinGeckoTokenName = (typeof coinGeckoTokenNames)[number];
Expand All @@ -41,6 +42,7 @@ const tokenNameToCoinGeckoTokenName: Record<TokenType, CoinGeckoTokenName> = {
ethereum: "ethereum",
solana: "solana",
kyve: "kyve-network",
matic: "matic-network"
};

type CoinGeckoResponse = Record<
Expand Down
6 changes: 6 additions & 0 deletions src/pricing/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,10 @@ export function weiToEth(wei: BigNumber.Value): BigNumber {
return BigNumber(wei).shiftedBy(-18);
}

export function baseToMatic(base: BigNumber.Value): BigNumber {
return BigNumber(base).shiftedBy(-18);
}

export function lamportsToSol(lamports: BigNumber.Value): BigNumber {
return BigNumber(lamports).shiftedBy(-9);
}
Expand All @@ -813,6 +817,8 @@ export function baseAmountToTokenAmount(
return lamportsToSol(amount);
case "kyve":
return ukyveToKyve(amount);
case "matic":
return baseToMatic(amount);
default:
return BigNumber(amount);
}
Expand Down
2 changes: 2 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
GatewayMap,
KyveGateway,
SolanaGateway,
MaticGateway
} from "./gateway";
import logger from "./logger";
import { MetricRegistry } from "./metricRegistry";
Expand Down Expand Up @@ -99,6 +100,7 @@ export async function createServer(
ethereum: new EthereumGateway(),
solana: new SolanaGateway(),
kyve: new KyveGateway(),
matic: new MaticGateway()
};

const emailProvider = (() => {
Expand Down
7 changes: 7 additions & 0 deletions src/utils/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ export function isValidEthAddress(address: string) {
return ethAddressRegex.test(address);
}

export function isValidMaticAddress(address: string) {
const maticAddressRegex = new RegExp("^0x[a-fA-F0-9]{40}$");
return maticAddressRegex.test(address);
}

export function isValidKyveAddress(address: string) {
const kyveAddressRegex = new RegExp("^kyve[a-zA-Z0-9]{39}$");
return kyveAddressRegex.test(address);
Expand All @@ -80,6 +85,8 @@ export function isValidUserAddress(
return isValidEthAddress(address);
case "kyve":
return isValidKyveAddress(address);
case "matic":
return isValidMaticAddress(address);
default:
return false;
}
Expand Down
13 changes: 13 additions & 0 deletions tests/helpers/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,19 @@ export const expectedTokenPrices = {
hkd: 0.181921,
brl: 0.126463,
},
"matic-network": {
usd: 0.368571,
jpy: 52.83,
eur: 0.331564,
gbp: 0.279491,
inr: 30.93,
aud: 0.546444,
sgd: 0.478965,
cad: 0.497505,
hkd: 2.87,
brl: 2.05,
}
}
};

// TODO: we could make this a function and apply it against the arweave rates above using the turboPercentageFee constant
Expand Down
4 changes: 4 additions & 0 deletions tests/helpers/testHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
EthereumGateway,
KyveGateway,
SolanaGateway,
MaticGateway,
} from "../../src/gateway";
import {
ArweaveBytesToWinstonOracle,
Expand Down Expand Up @@ -150,6 +151,9 @@ export const gatewayMap = {
kyve: new KyveGateway({
paymentTxPollingWaitTimeMs: 0,
}),
matic: new MaticGateway({
paymentTxPollingWaitTimeMs: 0,
})
};

export const testAddress = "-kYy3_LcYeKhtqNNXDN6xTQ7hW8S5EV0jgq_6j8a830"; // cspell:disable-line
Expand Down
2 changes: 1 addition & 1 deletion tests/router.int.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2197,7 +2197,7 @@ describe("Router tests", () => {
expect(data).to.deep.equal({ winc: "1000000000", balance: "1000000000" });
});

const tokens = ["arweave", "ethereum", "solana", "kyve"] as const;
const tokens = ["arweave", "ethereum", "solana", "kyve", "matic"] as const;

for (const token of tokens) {
it(`GET /account/balance/${token} returns 200 for valid params`, async () => {
Expand Down

0 comments on commit a807e4f

Please sign in to comment.