<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ilker Dagli</title>
    <description>The latest articles on DEV Community by Ilker Dagli (@ilkerdagli).</description>
    <link>https://dev.to/ilkerdagli</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1132391%2Fdf7442ba-1e47-49de-ab5c-448e8826f33a.jpeg</url>
      <title>DEV Community: Ilker Dagli</title>
      <link>https://dev.to/ilkerdagli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ilkerdagli"/>
    <language>en</language>
    <item>
      <title>Compiling and Deploying Smart Contract with AWS Lambda</title>
      <dc:creator>Ilker Dagli</dc:creator>
      <pubDate>Fri, 15 Mar 2024 11:05:39 +0000</pubDate>
      <link>https://dev.to/ilkerdagli/compiling-and-deploying-smart-contract-with-aws-lambda-3p76</link>
      <guid>https://dev.to/ilkerdagli/compiling-and-deploying-smart-contract-with-aws-lambda-3p76</guid>
      <description>&lt;p&gt;In this post, we'll walk through the process of compiling and deploying a smart contract on &lt;strong&gt;Ethereum's Sepolia Network&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;We'll use &lt;a href="https://www.infura.io/"&gt;Infura&lt;/a&gt; as the node provider and handle our operations from an AWS Lambda function. We'll also be using the AWS Secrets Manager to store/retrieve Ethereum Private Key.&lt;/p&gt;

&lt;p&gt;To start, we need to create a &lt;strong&gt;Lambda Python&lt;/strong&gt; function through CDK to compile and deploy the contract. One challenge I faced was installing &lt;strong&gt;solc&lt;/strong&gt;, as it requires write permission to the &lt;em&gt;$HOME&lt;/em&gt; path. I managed to bypass this issue by altering the &lt;em&gt;HOME&lt;/em&gt; enviroment variable, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as lambdaPython from "@aws-cdk/aws-lambda-python-alpha"
import * as lambda from "aws-cdk-lib/aws-lambda"
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager"

const INFURA_ENDPOINT = "https://sepolia.infura.io/v3/YOUR_API_KEY"
const SECRETS_ARN = "YOUR_SECRETS_ARN_THAT_STORES_ETH_PRIVATE_KEY"

const secrets = secretsmanager.Secret.fromSecretCompleteArn(
  this,
  "secrets",
  SECRETS_ARN
)

const compileAndDeployContractLambda = new lambdaPython.PythonFunction(
  this,
  "CompileAndDeployContractLambda",
  {
    entry: path.join(
      __dirname,
      "lambdaFunctions",
      "python",
      "compile-and-deploy-contract"
    ),
    bundling: {
      assetExcludes: [".venv"],
    },
    environment: {
      SECRETS_ARN: secrets.secretFullArn!,
      INFURA_ENDPOINT: INFURA_ENDPOINT,
      HOME: "/tmp", //Installing solc requires write permission to $HOME
    },
    runtime: lambda.Runtime.PYTHON_3_9,
    memorySize: 256,
    architecture: lambda.Architecture.X86_64,
    timeout: Duration.seconds(900),
    logRetention: logs.RetentionDays.SIX_MONTHS,
  }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll create our handler function. By default, the &lt;strong&gt;PythonFunction&lt;/strong&gt; of aws-lambda-python-alpha looks for an index.py file in the entry path unless another filename is specified. An important point to note here is that if a &lt;em&gt;requirements.txt&lt;/em&gt; file is present in the entry path, the PythonFunction will install the necessary packages specified in the file during the bundling process. Here's what your &lt;em&gt;requirements.txt&lt;/em&gt; might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web3==6.4.0
urllib3==1.26.15
py-solc-x==1.1.1
boto3==1.26.147
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following that, we'll create our Lambda function in the &lt;em&gt;lambdaFunctions/python/compile-and-deploy-contract/index.py&lt;/em&gt; path. Here's the code for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import boto3
import json
import logging
import traceback
from web3 import Web3
from eth_account import Account
from solcx import compile_source, install_solc

secretsArn = os.environ.get('SECRETS_ARN')
INFURA_ENDPOINT = os.environ.get('INFURA_ENDPOINT')

_solc_version = "0.8.17"
install_solc(_solc_version)
ethPrivKey = None
secretsClient = boto3.client('secretsmanager')

try:
    secrets = json.loads(secretsClient.get_secret_value(
        SecretId=secretsArn)['SecretString'])
    ethPrivKey = secrets['ETH_PRIV_KEY']
except Exception as e:
    logging.error('Cant retrieve secret from aws secrets manager:{}\n'.format(
        traceback.format_exc()))

def handler(event, context):
    contract = """
    // SPDX-License-Identifier: MIT
    // compiler version must be greater than or equal to 0.8.17 and less than 0.9.0
    pragma solidity ^0.8.17;

    contract HelloWorld {
        string public greet = "Hello World!";
    }
    """

    # Compile the contract
    compiledSolDict = compile_source(
        contract,
        output_values=['abi', 'bin'],
        solc_version=_solc_version,
        allow_paths='/opt'
    )

    compiledSolDict = compiledSolDict.get('&amp;lt;stdin&amp;gt;:HelloWorld')

    if compiledSolDict:
        # Deploy the contract
        binOutput = compiledSolDict['bin']
        abiOutput = compiledSolDict['abi']

        try:
            # Connect to the Ethereum network using Infura
            w3 = Web3(Web3.HTTPProvider(INFURA_ENDPOINT))

            # Create an account from the private key
            account = Account.from_key(ethPrivKey)

            # Set the default account for contract deployment
            w3.eth.default_account = account.address

            # Create a contract instance
            contract = w3.eth.contract(abi=abiOutput, bytecode=binOutput)

            # Build the transaction to deploy the contract
            transaction = contract.constructor().build_transaction({
                'nonce': w3.eth.get_transaction_count(account.address),
            })

            # Estimate the gas required for contract deployment
            gas_estimate = w3.eth.estimate_gas(transaction)

            # Update the transaction with the estimated gas
            transaction['gas'] = gas_estimate

            # Sign the transaction with the private key
            signed_transaction = account.sign_transaction(transaction)

            # Send the signed transaction to the network
            tx_hash = w3.eth.send_raw_transaction(
                signed_transaction.rawTransaction)

            # Wait for the transaction to be mined
            tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

            # Retrieve the contract address from the transaction receipt
            contractAddress = tx_receipt['contractAddress']
            logging.info(
                "Deployed contract address: {}".format(contractAddress))
        except:
            error = traceback.format_exc()
            logging.error('Failed to deploy contract:{}\n'.format(error))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code was designed to streamline contract deployments for a recent project. Unlike the code provided here, my project used separate Lambda function to compile Solidity files and store the outputs in S3. Another Lambda function then got triggered via S3's PUT Object event to deploy the compiled contracts. Another Lambda function &lt;em&gt;-which can be another post in the future-&lt;/em&gt; also verifies the deployed contract on &lt;br&gt;
&lt;a href="https://etherscan.io/"&gt;Etherscan&lt;/a&gt; using their API.&lt;/p&gt;

&lt;p&gt;This code has been simplified for this post and is untested, so feel free to reach out of you encounter any issues.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>aws</category>
      <category>smartcontracts</category>
    </item>
    <item>
      <title>Deploying a Next.js 14 App to AWS Amplify using CDK</title>
      <dc:creator>Ilker Dagli</dc:creator>
      <pubDate>Sat, 09 Mar 2024 23:43:21 +0000</pubDate>
      <link>https://dev.to/ilkerdagli/deploying-a-nextjs-14-app-to-aws-amplify-using-cdk-1e2g</link>
      <guid>https://dev.to/ilkerdagli/deploying-a-nextjs-14-app-to-aws-amplify-using-cdk-1e2g</guid>
      <description>&lt;p&gt;In this blog post, I'll guide you through deploying a Next.js 14 app to AWS Amplify using CDK. &lt;br&gt;
Please note that as of writing this post, AWS Amplify does not officially support Next.js 14.&lt;/p&gt;

&lt;p&gt;While I personally believe that &lt;a href="https://vercel.com"&gt;Vercel&lt;/a&gt; is a better solution for now, I encountered challenges deploying my app to Amplify. This post aims to help others who might face similar issues.&lt;/p&gt;

&lt;p&gt;Before we begin, ensure you have a GitHub access token obtained from the &lt;a href="https://github.com/settings/tokens"&gt;GitHub Settings Page&lt;/a&gt;. Store the token securely &lt;br&gt;
in AWS Secrets Manager.&lt;/p&gt;

&lt;p&gt;Let's start by creating an IAM role and a managed policy for use in the App construct of &lt;em&gt;aws_amplify_alpha&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const role = new cdk.aws_iam.Role(this, 'AmplifyRole', {
    assumedBy: new cdk.aws_iam.ServicePrincipal('amplify.amazonaws.com'),
    description: 'CDK Amplify Role',
})

const managedPolicy = cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
    'AdministratorAccess',
);

role.addManagedPolicy(managedPolicy)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create an Amplify App. Key details are explained below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const amplifyApp = new aws_amplify_alpha.App(this, 'MyApp', {
    description: 'My App',
    sourceCodeProvider: new aws_amplify_alpha.GitHubSourceCodeProvider({
        owner: 'ilkerdagli',
        repository: 'myApp',
        oauthToken: cdk.SecretValue.secretsManager('GithubAccessToken'),
    }),
    customRules: [
        {
            source: '/&amp;lt;*&amp;gt;',
            target: '/index.html',
            status: aws_amplify_alpha.RedirectStatus.NOT_FOUND_REWRITE,
        },
    ],
    environmentVariables: {
        "AMPLIFY_MONOREPO_APP_ROOT": "frontend",
        "AMPLIFY_DIFF_DEPLOY": "false",
        "_CUSTOM_IMAGE": "public.ecr.aws/docker/library/node:20.11",
        "NEXT_PUBLIC_AWS_AMPLIFY_REGION": "eu-central-1",
    },
    buildSpec: cdk.aws_codebuild.BuildSpec.fromObjectToYaml({
        version: '1.0',
        applications: [
            {
                appRoot: 'frontend',
                frontend: {
                    phases: {
                        preBuild: {
                            commands: ['yarn install --frozen-lockfile'],
                        },
                        build: {
                            commands: ['env | grep -e NEXT_PUBLIC_ &amp;gt;&amp;gt; .env.production', 'yarn run build'],
                        },
                    },
                    artifacts: {
                        baseDirectory: '.next',
                        files: ['**/*'],
                    },
                    cache: {
                        paths: ['node_modules/**/*', '.next/cache/**/*'],

                    },
                },
            }
        ]
    }),
    role: role,
})

const devBranch = amplifyApp.addBranch('dev', {
    description: 'dev branch',
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My app was located in the &lt;strong&gt;frontend&lt;/strong&gt; directory of my GitHub repository. Therefore, the environment variable AMPLIFY_MONOREPO_APP_ROOT_ is set to &lt;em&gt;frontend&lt;/em&gt;, &lt;br&gt;
which must match the buildSpec's applications &lt;strong&gt;appRoot&lt;/strong&gt; definition.&lt;/p&gt;

&lt;p&gt;Since Amplify's default build image doesn't support Next.js 14 due to an older node version, we use a custom image. Set the &lt;em&gt;CUSTOM_IMAGE&lt;/em&gt; environment variable accordingly.&lt;/p&gt;

&lt;p&gt;Additionally, we add a &lt;em&gt;dev&lt;/em&gt; branch to the app for automatic redeployment upon each push.&lt;/p&gt;

&lt;p&gt;To complete the setup, we need run the following commands to run the app on Amplify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws amplify update-app --app-id APPID --platform WEB_COMPUTE 
aws amplify update-branch --app-id APPID --branch-name dev --framework 'Next.js - SSR'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, for automation, consider using CDK custom resources as demonstrated below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new cdk.custom_resources.AwsCustomResource(this, 'AmplifyExtraSettings', {
    onUpdate: {
        service: 'Amplify',
        action: 'updateApp',
        parameters: {
            appId: amplifyApp.appId,
            platform: 'WEB_COMPUTE',
        },
        physicalResourceId: cdk.custom_resources.PhysicalResourceId.of(Date.now().toString()),
    },

    policy: cdk.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
        resources: [amplifyApp.arn],
    }),

});

new cdk.custom_resources.AwsCustomResource(this, 'AmplifyUpdateDevBranch', {
    onUpdate: {
        service: 'Amplify',
        action: 'updateBranch',
        parameters: {
            appId: amplifyApp.appId,
            branchName: 'dev',
            framework: 'Next.js - SSR',
        },
        physicalResourceId: cdk.custom_resources.PhysicalResourceId.of(Date.now().toString()),
    },
    policy: cdk.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
        resources: [amplifyApp.arn, devBranch.arn],
    }),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>cdk</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
