-
Notifications
You must be signed in to change notification settings - Fork 304
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
492 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# trunk-ignore-all(checkov/CKV_DOCKER_2) | ||
# trunk-ignore-all(checkov/CKV_DOCKER_3) | ||
# trunk-ignore-all(hadolint/DL3013) | ||
# trunk-ignore-all(hadolint/DL3033) | ||
FROM amazon/aws-lambda-python:3.10 | ||
|
||
RUN yum update -y && \ | ||
yum install -y gcc gmp-devel && \ | ||
yum clean all && \ | ||
rm -rf /var/cache/yum | ||
|
||
WORKDIR /var/task | ||
|
||
RUN pip install --no-cache-dir uv && uv venv | ||
|
||
COPY pyproject.toml ./ | ||
|
||
RUN pip install --no-cache-dir -e '.[lambda-dependencies]' | ||
|
||
COPY build ./build | ||
COPY deployments ./deployments | ||
COPY relayers.json ./relayers.json | ||
COPY .env ./ | ||
COPY fee_balancer.py ./ | ||
COPY constants.py ./kakarot_scripts/constants.py | ||
COPY starknet.py ./kakarot_scripts/utils/starknet.py | ||
COPY data ./kakarot_scripts/data | ||
|
||
CMD ["fee_balancer.lambda_handler"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import aws_cdk as cdk | ||
from fee_balancer_lambda_stack import FeeBalancerLambdaStack | ||
|
||
app = cdk.App() | ||
FeeBalancerLambdaStack(app, "FeeBalancerLambdaStack") | ||
|
||
app.synth() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
{ | ||
"app": "python3 app.py", | ||
"watch": { | ||
"include": ["**"], | ||
"exclude": [ | ||
"README.md", | ||
"cdk*.json", | ||
"requirements*.txt", | ||
"source.bat", | ||
"**/__init__.py", | ||
"**/__pycache__", | ||
"tests" | ||
] | ||
}, | ||
"context": { | ||
"@aws-cdk/aws-lambda:recognizeLayerVersion": true, | ||
"@aws-cdk/core:checkSecretUsage": true, | ||
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"], | ||
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, | ||
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, | ||
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, | ||
"@aws-cdk/aws-iam:minimizePolicies": true, | ||
"@aws-cdk/core:validateSnapshotRemovalPolicy": true, | ||
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, | ||
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, | ||
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, | ||
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true, | ||
"@aws-cdk/core:enablePartitionLiterals": true, | ||
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, | ||
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, | ||
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, | ||
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, | ||
"@aws-cdk/aws-route53-patters:useCertificate": true, | ||
"@aws-cdk/customresources:installLatestAwsSdkDefault": false, | ||
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, | ||
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, | ||
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, | ||
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, | ||
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, | ||
"@aws-cdk/aws-redshift:columnId": true, | ||
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, | ||
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, | ||
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, | ||
"@aws-cdk/aws-kms:aliasNameRef": true, | ||
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, | ||
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true, | ||
"@aws-cdk/aws-efs:denyAnonymousAccess": true, | ||
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, | ||
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, | ||
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, | ||
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, | ||
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, | ||
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, | ||
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, | ||
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, | ||
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, | ||
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, | ||
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, | ||
"@aws-cdk/aws-eks:nodegroupNameAttribute": true, | ||
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, | ||
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, | ||
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, | ||
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, | ||
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, | ||
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, | ||
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, | ||
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, | ||
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, | ||
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import asyncio | ||
import json | ||
import logging | ||
import os | ||
|
||
import boto3 | ||
from web3 import Web3 | ||
|
||
from kakarot_scripts.utils.starknet import ( | ||
get_balance, | ||
get_contract, | ||
get_eth_contract, | ||
get_starknet_account, | ||
wait_for_transaction, | ||
) | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel("INFO") | ||
|
||
client = boto3.client("secretsmanager") | ||
ssm = boto3.client("ssm") | ||
node_url = os.getenv("NODE_URL") | ||
web3 = Web3(Web3.HTTPProvider(node_url)) | ||
|
||
with open("coinbase_abi.json", "r") as f: | ||
coinbase_abi = json.load(f) | ||
|
||
contract_address = os.getenv("COINBASE_CONTRACT_ADDRESS") | ||
|
||
# Create smart contract instance | ||
contract = web3.eth.contract(address=contract_address, abi=coinbase_abi) | ||
|
||
|
||
def lambda_handler(event, context): | ||
return asyncio.get_event_loop().run_until_complete(check_and_fund_relayers()) | ||
|
||
|
||
async def check_and_fund_relayers(): | ||
# Load relayers information from JSON file | ||
with open("relayers.json", "r") as f: | ||
relayers = json.load(f) | ||
|
||
# Retrieve starknet secret from AWS Secrets Manager | ||
response = client.get_secret_value(SecretId="relayers_fund_account") | ||
secret_dict = json.loads(response["SecretString"]) | ||
|
||
starknet_address, starknet_private_key = next(iter(secret_dict.items())) | ||
starknet_account = await get_starknet_account( | ||
starknet_address, starknet_private_key | ||
) | ||
eth_contract = await get_eth_contract(starknet_account) | ||
|
||
# Retrieve eth secret from AWS Secrets Manager | ||
response = client.get_secret_value(SecretId="eth_coinbase_owner") | ||
secret_dict = json.loads(response["SecretString"]) | ||
|
||
eth_address, eth_private_key = next(iter(secret_dict.items())) | ||
nonce = web3.eth.get_transaction_count(eth_address) | ||
|
||
account_balance_before_withdraw = await get_balance( | ||
starknet_account.starknet_address, eth_contract | ||
) | ||
|
||
# withdraw fees from coinbase contract | ||
await withdraw_fee(starknet_address, nonce, eth_address, eth_private_key) | ||
|
||
account_balance = await get_balance(starknet_account.starknet_address, eth_contract) | ||
relayers_total_balance = await get_total_balance_of_relayers(relayers, eth_contract) | ||
|
||
actual_fee = account_balance - account_balance_before_withdraw | ||
|
||
# get the prev balances | ||
relayers_prev_total_balance = int(os.getenv("PREV_TOTAL_BALANCE")) | ||
|
||
# get the earning percentage | ||
earning_percentage = int(os.getenv("EARNING_PERCENTAGE")) | ||
|
||
cairo_counter = get_contract("Kakarot") | ||
|
||
yield cairo_counter | ||
|
||
base_fee = await cairo_counter.functions["get_base_fee"].call() | ||
logger.info(f"Base fee: {base_fee}") | ||
changed_fee = base_fee | ||
|
||
# check if the relayers balance is less than the prev balance | ||
if relayers_prev_total_balance + actual_fee < relayers_total_balance: | ||
# increase the base fee of 12.5% | ||
changed_fee += base_fee * 0.125 | ||
# check if the relayers balance is more than the prev balance + the acceptable earning percentage | ||
elif relayers_prev_total_balance + actual_fee > relayers_total_balance + ( | ||
earning_percentage * actual_fee / 100 | ||
): | ||
# decrease the base fee of 12.5% | ||
changed_fee -= base_fee * 0.125 | ||
else: | ||
logger.info("No changes to the base fee") | ||
|
||
if changed_fee != base_fee: | ||
tx = await cairo_counter.functions["set_base_fee"].invoke_v1(base_fee) | ||
await wait_for_transaction(tx.hash) | ||
|
||
return { | ||
"statusCode": 200, | ||
} | ||
|
||
|
||
async def get_total_balance_of_relayers(relayers, eth_contract, block_number): | ||
total_balance = 0 | ||
|
||
for relayer in relayers: | ||
account_balance = await get_balance( | ||
relayer["address"], eth_contract, block_number | ||
) | ||
total_balance += account_balance | ||
|
||
return total_balance | ||
|
||
|
||
async def withdraw_fee(starknet_address, nonce, eth_address, eth_private_key): | ||
Chain_id = web3.eth.chain_id | ||
|
||
# Call your function | ||
call_function = contract.functions.withdraw( | ||
toStarknetAddress=starknet_address | ||
).build_transaction({"chainId": Chain_id, "from": eth_address, "nonce": nonce}) | ||
|
||
# Sign transaction | ||
signed_tx = web3.eth.account.sign_transaction( | ||
call_function, private_key=eth_private_key | ||
) | ||
|
||
# Send transaction | ||
send_tx = web3.eth.send_raw_transaction(signed_tx.raw_transaction) | ||
|
||
# Wait for transaction receipt | ||
tx_receipt = web3.eth.wait_for_transaction_receipt(send_tx) | ||
logger.info(tx_receipt) | ||
|
||
|
||
if __name__ == "__main__": | ||
asyncio.run(check_and_fund_relayers()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import os | ||
import shutil | ||
|
||
from aws_cdk import Duration, Stack | ||
from aws_cdk import aws_events as events | ||
from aws_cdk import aws_events_targets as targets | ||
from aws_cdk import aws_iam as iam | ||
from aws_cdk import aws_lambda as _lambda | ||
from constructs import Construct | ||
|
||
|
||
class FeeBalancerLambdaStack(Stack): | ||
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: | ||
super().__init__(scope, construct_id, **kwargs) | ||
|
||
self.build_lambda_func() | ||
|
||
def build_lambda_func(self): | ||
node_url = os.environ.get("NODE_URL") | ||
if node_url is None: | ||
raise ValueError("NODE_URL environment variable is not set") | ||
|
||
coinbase_contract_address = os.environ.get("COINBASE_CONTRACT_ADDRESS") | ||
if coinbase_contract_address is None: | ||
raise ValueError( | ||
"COINBASE_CONTRACT_ADDRESS environment variable is not set" | ||
) | ||
|
||
prev_total_balance = os.environ.get("PREV_TOTAL_BALANCE") | ||
if prev_total_balance is None: | ||
raise ValueError("PREV_TOTAL_BALANCE environment variable is not set") | ||
|
||
earning_percentage = os.environ.get("EARNING_PERCENTAGE") | ||
if earning_percentage is None: | ||
raise ValueError("EARNING_PERCENTAGE environment variable is not set") | ||
|
||
dest_dir = "./" | ||
shutil.copy( | ||
"../../kakarot_scripts/constants.py", os.path.join(dest_dir, "constants.py") | ||
) | ||
shutil.copy( | ||
"../../kakarot_scripts/utils/starknet.py", | ||
os.path.join(dest_dir, "starknet.py"), | ||
) | ||
shutil.copy("../../.env", os.path.join(dest_dir, ".env")) | ||
shutil.copytree( | ||
"../../build", | ||
os.path.join(dest_dir, "build"), | ||
dirs_exist_ok=True, | ||
) | ||
shutil.copytree( | ||
"../../deployments", | ||
os.path.join(dest_dir, "deployments"), | ||
dirs_exist_ok=True, | ||
) | ||
shutil.copytree( | ||
"../../kakarot_scripts/data", | ||
os.path.join(dest_dir, "data"), | ||
dirs_exist_ok=True, | ||
) | ||
self.prediction_lambda = _lambda.DockerImageFunction( | ||
scope=self, | ||
id="fee_balancer_lambda", | ||
function_name="fee_balancer", | ||
code=_lambda.DockerImageCode.from_image_asset(directory="."), | ||
environment={ | ||
"NODE_URL": node_url, | ||
"COINBASE_CONTRACT_ADDRESS": coinbase_contract_address, | ||
"PREV_TOTAL_BALANCE": prev_total_balance, | ||
"EARNING_PERCENTAGE": earning_percentage, | ||
}, | ||
environment_encryption=None, | ||
timeout=Duration.minutes(1), | ||
) | ||
|
||
secret_arns = [ | ||
os.environ.get("RELAYERS_FUND_ACCOUNT_SECRET_ARN"), | ||
os.environ.get("ETH_COINBASE_OWNER_SECRET_ARN"), | ||
] | ||
secret_arns = [arn for arn in secret_arns if arn is not None] | ||
|
||
self.prediction_lambda.add_to_role_policy( | ||
iam.PolicyStatement( | ||
actions=["secretsmanager:GetSecretValue"], | ||
resources=secret_arns, | ||
) | ||
) | ||
|
||
# param_arns = [ | ||
# os.environ.get("RELAYERS_PREV_TOTAL_BALANCE_PARAM_ARN"), | ||
# os.environ.get("ACCOUNT_PREV_BALANCE_PARAM_ARN"), | ||
# os.environ.get("EARNING_PERCENTAGE_PARAM_ARN"), | ||
# ] | ||
# param_arns = [arn for arn in param_arns if arn is not None] | ||
|
||
# self.prediction_lambda.add_to_role_policy( | ||
# iam.PolicyStatement( | ||
# actions=["ssm:GetParameter", "ssm:PutParameter"], | ||
# resources=param_arns, | ||
# ) | ||
# ) | ||
|
||
events.Rule( | ||
self, | ||
"ScheduleRule", | ||
schedule=events.Schedule.cron(minute="0/20"), | ||
targets=[targets.LambdaFunction(self.prediction_lambda)], | ||
) |
Oops, something went wrong.