Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kmaus near/contract ping #16

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
npm-debug.log
.env
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/
dist/

.env
.env
.terraform*
23 changes: 23 additions & 0 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Use the official Node.js image as the base image
FROM node:16-alpine

# Set the working directory in the container
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Build the TypeScript code
RUN npm run build

# Specify the command to run the application
CMD ["node", "dist/server.js"]

# Expose the port the app runs on
EXPOSE 3000
118 changes: 118 additions & 0 deletions infra/dev/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
locals {
static_envs = {
}
}

resource "google_service_account" "service_account" {
account_id = "contract-ping-dev"
display_name = "contract-pinger-dev"
}

resource "google_project_iam_member" "sa-roles" {
for_each = toset([
"roles/run.invoker",
"roles/secretmanager.admin",
"roles/storage.objectAdmin",
"roles/logging.logWriter",
])

role = each.key
member = "serviceAccount:${google_service_account.service_account.email}"
project = var.project_id
}

resource "google_cloud_run_service" "contract_ping" {
provider = google-beta
name = "contract-pinger-dev"
location = "us-central1"
project = var.project_id
autogenerate_revision_name = true

template {
spec {
service_account_name = "contract-ping-dev@pagoda-discovery-platform-dev.iam.gserviceaccount.com"
containers {
args = ["node", "dist/server.js"]
image = "us-east1-docker.pkg.dev/pagoda-discovery-platform-dev/multichain/tools/contract-ping:latest"
ports {
name = "http1"
container_port = 3000
}
dynamic "env" {
for_each = local.static_envs
content {
name = env.key
value = env.value
}
}
env {
name = "NEXT_PUBLIC_NEAR_ACCOUNT_ID"
value_from {
secret_key_ref {
name = "contract_ping_near_account_id"
key = "latest"
}
}
}
env {
name = "NEXT_PUBLIC_NEAR_PRIVATE_KEY"
value_from {
secret_key_ref {
name = "contract_ping_near_private_key"
key = "latest"
}
}
}
env {
name = "NEXT_PUBLIC_CHAIN_SIGNATURE_CONTRACT"
value_from {
secret_key_ref {
name = "contract_ping_chain_sig_dev_contract_testnet"
key = "latest"
}
}
}
}
}
metadata {
annotations = {
"autoscaling.knative.dev/minScale" = "1"
"run.googleapis.com/cpu-throttling" = false
# "run.googleapis.com/vpc-access-connector" = "projects/pagoda-shared-infrastructure/locations/us-central1/connectors/dev-connector"
# "run.googleapis.com/vpc-access-egress" = "all-traffic"
}
}
}
traffic {
percent = 100
latest_revision = true
}

lifecycle {
# List of fields we don't want to see a diff for in terraform. Most of these fields are set
# by GCP and is metadata we don't want to account when considering changes in the service.
ignore_changes = [
template[0].metadata[0].labels["client.knative.dev/nonce"],
template[0].metadata[0].labels["run.googleapis.com/startupProbeType"],
template[0].metadata[0].annotations["run.googleapis.com/client-name"],
]
}
depends_on = [ google_service_account.service_account ]
}

data "google_iam_policy" "noauth" {
binding {
role = "roles/run.invoker"
members = [
"allUsers",
]
}
}

resource "google_cloud_run_service_iam_policy" "noauth" {
location = google_cloud_run_service.contract_ping.location
project = google_cloud_run_service.contract_ping.project
service = google_cloud_run_service.contract_ping.name

policy_data = data.google_iam_policy.noauth.policy_data
}
15 changes: 15 additions & 0 deletions infra/dev/resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
terraform {
backend "gcs" {
bucket = "multichain-terraform-dev"
prefix = "state/tools/contract-ping"
}
}

provider "google" {
project = "pagoda-discovery-platform-dev"
}

provider "google" {
project = "pagoda-shared-infrastructure"
alias = "something"
}
4 changes: 4 additions & 0 deletions infra/dev/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
variable "project_id" {
default = "pagoda-discovery-platform-dev"
description = "The default project id to use for resources in this directory."
}
120 changes: 120 additions & 0 deletions infra/prod/mainnet.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
locals {
static_envs_mainnet = {
"NEXT_PUBLIC_NETWORK_ID": "mainnet"
}

}

resource "google_service_account" "service_account_mainnet" {
account_id = "contract-ping-mainnet"
display_name = "contract-pinger-mainnet"
}

resource "google_project_iam_member" "sa-roles_mainnet" {
for_each = toset([
"roles/run.invoker",
"roles/secretmanager.admin",
"roles/storage.objectAdmin",
"roles/logging.logWriter",
])

role = each.key
member = "serviceAccount:${google_service_account.service_account_mainnet.email}"
project = var.project_id
}

resource "google_cloud_run_service" "contract_ping_mainnet" {
provider = google-beta
name = "contract-pinger-mainnet"
location = "us-central1"
project = var.project_id
autogenerate_revision_name = true

template {
spec {
service_account_name = "contract-ping-mainnet@pagoda-discovery-platform-prod.iam.gserviceaccount.com"
containers {
args = ["node", "dist/server.js"]
image = "us-east1-docker.pkg.dev/pagoda-discovery-platform-prod/multichain/tools/contract-ping:latest"
ports {
name = "http1"
container_port = 3000
}
dynamic "env" {
for_each = local.static_envs_mainnet
content {
name = env.key
value = env.value
}
}
env {
name = "NEXT_PUBLIC_NEAR_ACCOUNT_ID"
value_from {
secret_key_ref {
name = "contract_ping_near_account_id_mainnet"
key = "latest"
}
}
}
env {
name = "NEXT_PUBLIC_NEAR_PRIVATE_KEY"
value_from {
secret_key_ref {
name = "contract_ping_near_private_key_mainnet"
key = "latest"
}
}
}
env {
name = "NEXT_PUBLIC_CHAIN_SIGNATURE_CONTRACT"
value_from {
secret_key_ref {
name = "contract_ping_chain_sig_contract_mainnet"
key = "latest"
}
}
}
}
}
metadata {
annotations = {
"autoscaling.knative.dev/minScale" = "1"
"run.googleapis.com/cpu-throttling" = false
# "run.googleapis.com/vpc-access-connector" = "projects/pagoda-shared-infrastructure/locations/us-central1/connectors/dev-connector"
# "run.googleapis.com/vpc-access-egress" = "all-traffic"
}
}
}
traffic {
percent = 100
latest_revision = true
}

lifecycle {
# List of fields we don't want to see a diff for in terraform. Most of these fields are set
# by GCP and is metadata we don't want to account when considering changes in the service.
ignore_changes = [
template[0].metadata[0].labels["client.knative.dev/nonce"],
template[0].metadata[0].labels["run.googleapis.com/startupProbeType"],
template[0].metadata[0].annotations["run.googleapis.com/client-name"],
]
}
depends_on = [ google_service_account.service_account_mainnet ]
}

data "google_iam_policy" "noauth_mainnet" {
binding {
role = "roles/run.invoker"
members = [
"allUsers",
]
}
}

resource "google_cloud_run_service_iam_policy" "noauth_mainnet" {
location = google_cloud_run_service.contract_ping_mainnet.location
project = google_cloud_run_service.contract_ping_mainnet.project
service = google_cloud_run_service.contract_ping_mainnet.name

policy_data = data.google_iam_policy.noauth_mainnet.policy_data
}
15 changes: 15 additions & 0 deletions infra/prod/resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
terraform {
backend "gcs" {
bucket = "terraform-prod-multichain"
prefix = "state/tools/contract-ping"
}
}

provider "google" {
project = "pagoda-discovery-platform-prod"
}

provider "google" {
project = "pagoda-shared-infrastructure"
alias = "something"
}
120 changes: 120 additions & 0 deletions infra/prod/testnet.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
locals {
static_envs_testnet = {
"NEXT_PUBLIC_NETWORK_ID": "testnet"
}

}

resource "google_service_account" "service_account_testnet" {
account_id = "contract-ping-testnet"
display_name = "contract-pinger-testnet"
}

resource "google_project_iam_member" "sa-roles_testnet" {
for_each = toset([
"roles/run.invoker",
"roles/secretmanager.admin",
"roles/storage.objectAdmin",
"roles/logging.logWriter",
])

role = each.key
member = "serviceAccount:${google_service_account.service_account_testnet.email}"
project = var.project_id
}

resource "google_cloud_run_service" "contract_ping_testnet" {
provider = google-beta
name = "contract-pinger-testnet"
location = "us-central1"
project = var.project_id
autogenerate_revision_name = true

template {
spec {
service_account_name = "contract-ping-testnet@pagoda-discovery-platform-prod.iam.gserviceaccount.com"
containers {
args = ["node", "dist/server.js"]
image = "us-east1-docker.pkg.dev/pagoda-discovery-platform-prod/multichain/tools/contract-ping:latest"
ports {
name = "http1"
container_port = 3000
}
dynamic "env" {
for_each = local.static_envs_testnet
content {
name = env.key
value = env.value
}
}
env {
name = "NEXT_PUBLIC_NEAR_ACCOUNT_ID"
value_from {
secret_key_ref {
name = "contract_ping_near_account_id_testnet"
key = "latest"
}
}
}
env {
name = "NEXT_PUBLIC_NEAR_PRIVATE_KEY"
value_from {
secret_key_ref {
name = "contract_ping_near_private_key_testnet"
key = "latest"
}
}
}
env {
name = "NEXT_PUBLIC_CHAIN_SIGNATURE_CONTRACT"
value_from {
secret_key_ref {
name = "contract_ping_chain_sig_contract_testnet"
key = "latest"
}
}
}
}
}
metadata {
annotations = {
"autoscaling.knative.dev/minScale" = "1"
"run.googleapis.com/cpu-throttling" = false
# "run.googleapis.com/vpc-access-connector" = "projects/pagoda-shared-infrastructure/locations/us-central1/connectors/dev-connector"
# "run.googleapis.com/vpc-access-egress" = "all-traffic"
}
}
}
traffic {
percent = 100
latest_revision = true
}

lifecycle {
# List of fields we don't want to see a diff for in terraform. Most of these fields are set
# by GCP and is metadata we don't want to account when considering changes in the service.
ignore_changes = [
template[0].metadata[0].labels["client.knative.dev/nonce"],
template[0].metadata[0].labels["run.googleapis.com/startupProbeType"],
template[0].metadata[0].annotations["run.googleapis.com/client-name"],
]
}
depends_on = [ google_service_account.service_account_testnet ]
}

data "google_iam_policy" "noauth_testnet" {
binding {
role = "roles/run.invoker"
members = [
"allUsers",
]
}
}

resource "google_cloud_run_service_iam_policy" "noauth_testnet" {
location = google_cloud_run_service.contract_ping_testnet.location
project = google_cloud_run_service.contract_ping_testnet.project
service = google_cloud_run_service.contract_ping_testnet.name

policy_data = data.google_iam_policy.noauth_testnet.policy_data
}
3 changes: 3 additions & 0 deletions infra/prod/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
variable "project_id" {
default = "pagoda-discovery-platform-prod"
}
827 changes: 791 additions & 36 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -30,15 +30,17 @@
"jest": "^29.7.0",
"prettier": "^3.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.4.3"
"typescript": "^5.5.4"
},
"dependencies": {
"@types/express": "^4.17.21",
"axios": "^1.6.8",
"bitcoinjs-lib": "^6.1.5",
"bn.js": "^5.2.1",
"coinselect": "^3.1.13",
"elliptic": "^6.5.5",
"ethers": "^6.11.1",
"express": "^4.19.2",
"js-sha3": "^0.9.3",
"near-api-js": "^3.0.4"
}
68 changes: 68 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// import dotenv from 'dotenv';
// dotenv.config();
require("dotenv").config()

import express, { Request, Response } from 'express';
import { ethers } from 'ethers';
import { sign } from './src/signature/signature';
import { NearAuthentication, ChainSignatureContracts } from './src/chains/types';
import { KeyPair } from 'near-api-js';
import { fetchDerivedEVMAddress } from './src';


const app = express();
const port = process.env.PORT || 3000;

app.use(express.json());

app.post('/ping', async (req, res) => {
const transactionHash = ethers.randomBytes(32);
const path = "m/44'/60'/0'/0/0";
const nearAuthentication: NearAuthentication = {
accountId: process.env.NEXT_PUBLIC_NEAR_ACCOUNT_ID || '',
keypair: KeyPair.fromString(
process.env.NEXT_PUBLIC_NEAR_PRIVATE_KEY || ''
),
networkId: process.env.NEXT_PUBLIC_NETWORK_ID || '',
};
const contract: ChainSignatureContracts =
process.env.NEXT_PUBLIC_CHAIN_SIGNATURE_CONTRACT || '';

try {
const signature = await sign({
transactionHash,
path,
nearAuthentication,
contract,
});

const ethereumAddress = await fetchDerivedEVMAddress(
nearAuthentication.accountId,
path,
nearAuthentication.networkId,
contract
);

const recoveredAddress = ethers.recoverAddress(transactionHash, {
r: `0x${signature.r}`,
s: `0x${signature.s}`,
v: signature.v,
});

if (recoveredAddress.toLowerCase() === ethereumAddress.toLowerCase()) {
res.json({ signature, recoveredAddress, ethereumAddress });
} else {
res.status(500).json({ error: 'Recovered address does not match' });
}
} catch (error) {
if (error instanceof Error) {
res.status(500).json({ error: error.message });
} else {
res.status(500).json({ error: 'An unknown error occurred' });
}
}
});

app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
4 changes: 2 additions & 2 deletions src/chains/types.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ export interface ChainProvider {
}

export interface NearAuthentication {
networkId: 'testnet' | 'mainnet'
networkId: string
keypair: KeyPair
accountId: string
}
@@ -31,5 +31,5 @@ interface FailureResponse {

export type Response = SuccessResponse | FailureResponse

export type NearNetworkIds = 'mainnet' | 'testnet'
export type NearNetworkIds = string
export type BTCNetworkIds = 'mainnet' | 'testnet'
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {

/* Visit https://aka.ms/tsconfig to read more about this file */

/* Projects */
@@ -39,7 +40,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
"resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */