Blockchain Developer | BSc Computer Science

Author: ericnordelo

Ethereum references guide

The idea of this post is to have organized references to what I consider are some of the “must read sometime” articles related to the field. Is also a references guide for myself, and I will try to keep it as updated as possible.

EVM Opcodes list with Static Gas Costs

This post is a reference guide from the Ethereum organization, where you can find an updated opcode list (from the EVM assembly) with gas costs for opcodes with static gas consumption. I think that every Ethereum Blockchain Developer should understand how EVM works and the gas costs associated with the bytecode operations, so in my opinion, this must be on this list.

EVM Dynamic Gas Costs

This post is a complement to the previous one. Here we can find a list of the gas costs of the opcodes that are computed dynamically depending on the input or extra considerations. Ex: Refunds, Access List, LOG (events), etc…

Understanding gas costs after Berlin hard fork

This article wasn’t written by me, but I found it so good that I wanted to keep it on my blog for reference and to share it with people on my side. To check the original one click here. I got permission from the original author to publish it.

The Berlin hard fork introduced four new EIPs. Two of them (EIP-2929 and EIP-2930) affect how the gas cost of a transaction is computed. This article explains how (some) gas costs were calculated before Berlin, how this will change with EIP-2929, and how to use the Access Lists feature introduced by EIP-2930.

tl;dr

This is a long article, so you can just read this and close the tab:

  • The Berlin hard fork changes the gas cost of some opcodes. If you have a hardcoded gas value in a dapp or a smart contract, they might stop working. And if this happens and the smart contract is non-upgradeable, consumers will need to employ access lists (EIP-2930) to use it.
  • Access lists can be used to reduce gas costs a little, but they could actually increase the total gas consumed in some scenarios.
  • geth includes a new RPC method called eth_createAccessList to simplify the creation of access lists.

Gas costs before Berlin

Each opcode executed by the EVM has an associated gas cost. For most of them, this cost is fixed: PUSH1 always consumes 3 units of gas, MUL consumes 5, and so on. For others it is variable: the SHA3 opcode’s cost depends on the size of its input, for example.

We’ll focus on the SLOAD and SSTORE opcodes since they are the ones most affected by the Berlin hard fork. We’ll talk later about those that target an address, like all the EXT* and CALL* opcodes, since their gas cost changes too.

SLOAD before Berlin

Without EIP-2929, the cost of SLOAD is simple: it always costs 800 of gas. So there isn’t much to say about it (for now).

SSTORE before Berlin

SSTORE is maybe the most complex opcode in terms of gas because its cost depends on things like the current value of the storage slot, the new value, and whether it was previously modified. We’ll analyze only some scenarios to get a basic understanding; if you want to learn more, read the EIPs linked at the end of this article.

  • If the value of the slot changes from 0 to 1 (or any non-zero value), the cost is 20000
  • If the value of the slot changes from 1 to 2 (or any other non-zero value), the cost is 5000.
  • If the value of the slot changes from 1 (or any non-zero value) to 0, the cost is also 5000, but you get a gas refund at the end of the transaction. We won’t cover refunds in this article since they are not affected by Berlin.
  • If the value was previously modified during the same transaction, all the subsequent SSTOREs cost 800.

The details are not that interesting here. The important part is that SSTORE is expensive and that its cost depends on several factors.

Gas costs after EIP-2929

EIP-2929 changes all these values. But before getting into that, we need to talk about an important concept introduced by the EIP: accessed addresses and accessed storage keys.

An address or a storage key is considered accessed if it was previously “used” during the transaction. For example, when you CALL another contract, the address of that contract is marked as accessed. Similarly, when you SLOAD or SSTORE some slot, it will be considered accessed for the rest of the transaction. It doesn’t matter which opcode did it: if an SLOAD reads a slot, it will be considered accessed for both the next SLOADs and the next SSTOREs.

Something important to notice here is that storage keys are “inside” some address. As the EIP explains it:

When executing a transaction, maintain a set accessed_addresses: Set[Address] and accessed_storage_keys: Set[Tuple[Address, Bytes32]]

That is, when we say that a storage slot is accessed, we are actually saying that a pair (address, storageKey) was accessed.

With that being said, let’s talk about the new gas costs.

SLOAD after Berlin

Before Berlin, SLOAD had a fixed cost of 800. Now, it depends on whether that storage slot was already accessed or not. If it wasn’t accessed, the cost is 2100; if it was, it is 100. So, an SLOAD costs 2000 less if the slot is in the list of accessed storage keys.

SSTORE after Berlin

Let’s revisit our previous SSTORE examples in the context of EIP-2929:

  • If the value of the slot changes from 0 to 1 (or any non-zero value), the cost is:
    • 22100 if the storage key wasn’t accessed
    • 20000 if it was
  • If the value of the slot changes from 1 to 2 (or any other non-zero value), the cost is:
    • 5000 if the storage key wasn’t accessed
    • 2900 if it was
  • If the value of the slot changes from 1 (or any non-zero value) to 0, the cost is the same as the previous item, plus the refund.
  • If the value was previously modified during the same transaction, all the subsequent SSTOREs cost 100.

As you can see, the first SSTORE costs 2100 less if the slot it’s modifying was previously accessed.

Putting it all together

That was a mouthful, so here’s a table comparing all the values mentioned so far:

OPCODEBefore BerlinAfter Berlin
Not accessedAccessed
SLOAD8002100100
SSTORE from 0 to 1200002210020000
SSTORE from 1 to 2500050002900
SLOAD + SSTORE*580050003000
SSTORE* + SLOAD580051003000
SSTORE of an already written slot800100100
*From a non-zero value to a different non-zero value, like in the third row

Notice that in the last row it doesn’t make sense to talk about the slot having been accessed or not because, if it was previously written, then it was also accessed.

EIP-2930: Optional Access List transactions

The other EIP we mentioned at the beginning was EIP-2930. This EIP adds a new type of transaction that can include an access list in the transaction payload. This means that you can declare beforehand which addresses and slots should be considered as accessed before the transaction’s execution starts. For example, an SLOAD of a non-accessed slot costs 2100, but if that slot was included in the transaction’s access list, then that same opcode will cost 100.

But if the gas costs are lower when an address or storage key is already accessed, does this mean that we can add everything to the transaction’s access list and get a gas reduction? Yay, free gas! Well, not exactly, because you also need to pay gas for each address and each storage key that you add.

Let’s see an example. Say we are sending a transaction to contract A. An access list could look like this:

accessList: [{
  address: "<address of A>",
  storageKeys: [
    "0x0000000000000000000000000000000000000000000000000000000000000000"
  ]
}]

If we send a transaction with this access list, and the first opcode that uses the 0x0 slot is a SLOAD, it will cost 100 instead of 2100. That’s a gas reduction of 2000. But each storage key included in the transaction’s access list has a cost of 1900. So we only save 100 of gas. (If the first opcode to access that slot is an SSTORE instead, we would save 2100 of gas, which means that we’d save 200 of gas in total if we consider the cost of the storage key.)

Does this mean that we always save gas when using transaction’s with access lists? Well, no, because we also pay gas for the address in the access list ("<address of A>" in our example).

Accessed addresses

So far, we’ve been talking only about the SLOAD and SSTORE opcodes, but those aren’t the only ones that change after Berlin. For example, the CALL opcode had a fixed cost of 700. But after EIP-2929 its cost is 2600 if the address is not in the access list and 100 if it is. And, like the accessed storage keys, it doesn’t matter what OPCODE accessed that address before (for example, if an EXTCODESIZE was called first, then that opcode will cost 2600, and any subsequent EXTCODESIZECALLSTATICCALL that uses the same address will cost 100).

How is this affected by transactions with access lists? For example, if we send a transaction to contract A, and that contract calls another contract B, then we can include an access list like this:

accessList: [{ address: "<address of B>", storageKeys: [] }]

We’ll have to pay a cost of 2400 to include this access list in the transaction, but then the first opcode that uses the address of B will cost 100 instead of 2600. So we saved 100 of gas by doing this. And if B uses its storage somehow and we know which keys it will use, then we can also include them in the access list and save 100/200 of gas for each one (depending on whether the first opcode is an SLOAD or an SSTORE).

But why are we talking about another contract? What happens with the contract that we are calling? Why don’t we do this?

accessList: [
  {address: "<address of A>", storageKeys: []},
  {address: "<address of B>", storageKeys: []},
]

We could do it, but it wouldn’t be worth it because EIP-2929 specifies that the address of the contract that is being called (that is, tx.to) is always included in the accessed_addresses list. So we are paying 2400 more for nothing.

Let’s analyze our example of the previous section again:

accessList: [{
  address: "<address of A>",
  storageKeys: [
    "0x0000000000000000000000000000000000000000000000000000000000000000"
  ]
}]

This will actually be wasteful unless we include several storage keys more. If we assume that a storage key is always used first by an SLOAD, then we need at least 24 storage keys just to break even.

As you can imagine, analyzing this and creating an access list by hand is not fun. Luckily, there is a better way.

The eth_createAccessList RPC method

Geth (starting from version 1.10.2) includes a new eth_createAccessList RPC method that you can use to generate access lists. It is used like eth_estimateGas, but instead of a gas estimation, it returns something like this:

{
  "accessList": [
    {
      "address": "0xb0ee076d7779a6ce152283f009f4c32b5f88756c",
      "storageKeys": [
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000001"
      ]
    }
  ],
  "gasUsed": "0x8496"
}

That is, it gives you the list of addresses and storage keys that will be used by that transaction, plus the gas consumed if the access list is included. (And, like eth_estimateGas, this is an estimation; the list could change when the transaction is actually mined.) But, again, this doesn’t mean that this gas will be lower than the gas used if you just send the same transaction without an access list!

I suppose we’ll discover over time what’s the proper way of doing this, but my pseudo-code guess is this:

let gasEstimation = estimateGas(tx)
let { accessList, gasUsed } = createAccessList(tx)
if (gasUsed > gasEstimation) {
  delete accessList[tx.to]
}
tx.accessList = accessList;
sendTransaction(tx)

Unbricking contracts

It is important to mention that the main purpose of Access Lists is not to use gas. As the EIP explains it:

Mitigates contract breakage risks introduced by EIP-2929, as transactions could pre-specify and pre-pay for the accounts and storage slots that the transaction plans to access; as a result, in the actual execution, the SLOAD and EXT* opcodes would only cost 100 gas: low enough that it would not only prevent breakage due to that EIP but also “unstuck” any contracts that became stuck due to EIP 1884.

This means that if a contract makes assumptions about the cost of executing something, the increase in gas costs could break it. For example, a contract makes a call to another contract like someOtherContract.someFunction{gas: 34500}() because it assumes that someFunction will use exactly 34500 of gas, then it will break. But if you include the proper access list in the transaction, then the contract will work again.

Check it yourself

If you want to test this yourself, clone this repo which has several examples that can be executed using Hardhat and geth. Check the README for the instructions.

Was this fun?

If this low-level mumbo-jumbo is your idea of fun, you should know that Nomic Labs is hiring.

References

  • EIP-2929 and EIP-2930 are the two Berlin EIPs relevant to this article.
  • EIP-2930 depends on another part of Berlin: EIP-2718, also called Typed Transactions.
  • EIP-2929 refers to EIP-2200 a lot, so if you want to understand gas costs more in depth, you should start there.
  • For more complex examples comparing how gas usage changes, check this.

ERC20: The Ethereum Token Standard

What is a token in the Ethereum Ecosystem?

In Ethereum, tokens are simply digital assets that are being built on top of the network. They benefit from Ethereum’s existing infrastructure instead of developers having to build an entirely new blockchain. They are usually digital abstractions of fungible coins, that can be used for exchanging and payments. There are a lot of types of tokens, from stable coins like USDT and USDC, covering NFTs and coins with volatility.

The ERC20 standard

Check this post if you don’t understand what are ERCs and EIPs in the Ethereum Ecosystem.

ERC-20 is one of the most widely-used standards for smart contracts on the Ethereum platform. The fact that nearly all Ethereum tokens are issued to the same standard brings vast benefits for users of the Ethereum ecosystem. It means that ERC-20 tokens are interoperable with one another and with any ERC-20 supported contract, exchange, marketplace, or wallet.

If you have previous experience with Object-Oriented Programming, you can think of ERC20 as an interface. The standard is just a collection of methods that a token contract in Solidity should implement.

What are the Methods of the ERC-20 Token Standard Interface?

The ERC-20 token standard is relatively straightforward, comprising nine rules for issuing tokens. Six of them are mandatory and must be applied in all instances. Three are optional, although at least two of these are generally used.

Optional Rules

The three optional rules are:

  • name: Despite being optional, this is almost always used as token owners want users to be able to identify their tokens.
  • symbol: Again, almost always used as exchanges list token pairs using tickers.
  • decimal: Each ERC-20 token can be denominated in fractional units of up to one-eighteenth.

Mandatory Rules

Mandatory rules are categorized as either functions or events. The first two functions don’t modify the state of the contract. They define some core features of the token, and if queried, will return already-defined information.

  • totalSupply: the total number of the tokens issued.
  • balanceOf: answers a query regarding how many tokens any given address is holding.

The rest of the functionalities modifies the contract state except the allowance method.

  • transfer: allow a holder account to transfer its balance to another account.
  • approve: approves the movement of tokens in a transferFrom by a third party.
  • allowance: the amount of tokens approved to be transfered by a third party.
  • transferFrom: once approved, this function instructs the contract to move a defined value of tokens from a sender address to a recipient address (checking the allowance).

Summarizing

ERC-20 is one of the most widely-used standards for smart contracts on the Ethereum platform. All ERC-20 tokens are issued to the same standard. This ability to integrate has acted as a positive reinforcement cycle for Ethereum, with the existing ecosystem pulling in more developers and users who want to join in.

Ethereum Ecosystem: EIPs and ERCs concepts explained

What is an EIP?

EIP stands for Ethereum Improvement Proposal. These are design documents informing the Ethereum community, particularly its core developers, about new features proposals on Ethereum or changes to the ecosystem’s structure, processes, or environment.

EIPs are designed to provide both precise technical specifications of a new feature and rationale for adopting it. They’re a little like technical white papers. The person who brings up an EIP is responsible for persuading and convincing others in the community and building consensus around the new proposal.

EIP structure

Most successful EIPs are laid out like this:

  • Preamble
  • Simple Summary
  • Abstract
  • Motivation
  • Specification
  • Rationale
  • Backwards Compatibility
  • Test Cases
  • Implementations
  • Copyright Waiver

What is an ERC?

ERC stands for Ethereum Request for Comment, and “Request for Comment” is a similar concept to that devised by the Internet Engineering Task Force as a means of conveying essential technical notes and requirements to a group of developers and users.

In practice, it is just a subtype of Standards Track EIP, which stands for application-level standards and conventions, including contract standards such as token standards (ERC-20), name registries (ERC-137), URI schemes, library/package formats, and wallet formats.

To dive deeper into the types of EIPs and how to commit one, go to the official definition page in EIP-1.

SOLID: The First Object-Oriented Design Principles through Examples

SOLID is an acronym for the first five object-oriented programming (OOP) principles by Robert C. Martin.

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

These principles are considered by many people The Bible of the OOP, and every good and experienced programmer should know about them (and also about Design Patterns). In this post, we are going to explore these principles one by one through examples for better understanding.

In this post I’m going to use python for the examples.

The First Letter: Single Responsibility Principle

A class should have one and only one reason to change, meaning that a class should have only one job.

Robert c. Martin

To exemplify, let’s take a class that calculates the sum of the areas of a series of figures (Circles and Squares).

import math


class Square:
    def __init__(self, side_length):
        self.side_length = side_length


class Circle:
    def __init__(self, radius):
        self.radius = radius


class AreaCalculator:
    def __init__(self, shapes):
        self.shapes = shapes

    def sum(self):
        total_area = 0
        for shape in self.shapes:
            if type(shape) == Square:
                total_area += shape.side_length ** 2
            elif type(shape) == Circle:
                total_area += math.pi * shape.radius ** 2

        return total_area

    def pretty_sum(self):
        return 'The total area is %f' % self.sum()

The code is easy to understand since it only uses the basic formulas for ​​a square or circle area correspondingly, but note that the AreaCalculator class also displays a more descriptive message for the user using the pretty_sum method.

What if we want to change the format of this message later to JSON to be used as a response to an HTTP request? we have to modify the code of the AreaCalculator, even though the logic of calculating the areas does not vary. This goes against the Single-Responsibility principle since the AreaCalculator should only be concerned about calculating the area.

The logic for outputting should be handled in a separate class

(...)
class AreaCalculator:
    def __init__(self, shapes):
        self.shapes = shapes

    def sum(self):
        total_area = 0
        for shape in self.shapes:
            if type(shape) == Square:
                total_area += shape.side_length ** 2
            elif type(shape) == Circle:
                total_area += math.pi * shape.radius ** 2

        return total_area


class CalculatorOutputter:
    def __init__(self, calculator):
        self.calculator = calculator

    def pretty_sum(self):
        return 'The total area is %f' % self.calculator.sum()

    def json(self):
        return {'sum': self.calculator.sum()}

Open-Closed Principle

Objects or entities should be open for extension but closed for modification.

Robert c. Martin

This means that, whenever possible, a class should be extendable without modifying the class itself.

Let’s analize again the AreaCalculator

(...)
class AreaCalculator:
    def __init__(self, shapes):
        self.shapes = shapes

    def sum(self):
        total_area = 0
        for shape in self.shapes:
            if type(shape) == Square:
                total_area += shape.side_length ** 2
            elif type(shape) == Circle:
                total_area += math.pi * shape.radius ** 2

        return total_area

Suppose we want to add triangles to our program. For our AreaCalculator to continue working properly, its code must be modified to use the appropriate formula for the new shape.

(...)
class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height


class AreaCalculator:
    def __init__(self, shapes):
        self.shapes = shapes

    def sum(self):
        total_area = 0
        for shape in self.shapes:
            if type(shape) == Square:
                total_area += shape.side_length ** 2
            elif type(shape) == Circle:
                total_area += math.pi * shape.radius ** 2
            elif type(shape) == Triangle:
                total_area += shape.base * shape.height / 2

        return total_area

Every time we want to add a new shape we have to modify the code, adding more if/else statements to the sum method. This goes against the Open-Closed Principle and is very difficult to maintain. One solution is to delegate the calculation of the area to the shape itself and use this method in the AreaCalculator

(...)
class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return self.base * self.height / 2


class AreaCalculator:
    def __init__(self, shapes):
        self.shapes = shapes

    def sum(self):
        total_area = 0
        for shape in self.shapes:
            total_area += shape.area()

        return total_area

Now we can add new figures without modifying the code of the AreaCalculator class, and it will continue working as expected.

Liskov Substitution Principle

Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.

ROBERT C. MARTIN

This means that every subclass or derived class should be substitutable for their base or parent class.

Building off the example AreaCalculator class, consider a new VolumeCalculator class that extends the AreaCalculator class:

(...)
class VolumeCalculator(AreaCalculator):
    def __init__(self, shapes):
        super().__init__(shapes)

    def sum(self):
        total_volume = 0
        for shape in self.shapes:
            # assume volume method implemented in shapes
            total_volume += shape.volume()

        return [total_volume]

Recall that the CalculatorOutputter class resembles this:

(...)
class CalculatorOutputter:
    def __init__(self, calculator):
        self.calculator = calculator

    def pretty_sum(self):
        return 'The total area is %f' % self.calculator.sum()

    def json(self):
        return {'sum': self.calculator.sum()}

If you tried to run an example like this:

areas = new AreaCalculator(shapes);
volumes = new VolumeCalculator(solidShapes);

output = new CalculatorOutputter(areas);
output2 = new CalculatorOutputter(volumes);

When you call the pretty_sum method on the output2 object, you will get an error informing you of an array to float conversion.

To fix this, instead of returning an array from the VolumeCalculator class sum method, return total_volume

(...)
class VolumeCalculator(AreaCalculator):
    def __init__(self, shapes):
        super().__init__(shapes)

    def sum(self):
        total_volume = 0
        for shape in self.shapes:
            # assume volume method implemented in shapes
            total_volume += shape.volume()

        return total_volume

The total_volume should be a float or integer.

That satisfies the Liskov substitution principle.

Interface Segregation Principle

A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.

ROBERT C. MARTIN
(...)
class ShapeInterface:
    def area(self) -> float:
        pass

    def volume(self) -> float:
        pass


class Triangle(ShapeInterface):
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return self.base * self.height / 2

    def volume(self):
        return 0

(...)

Let’s build from the previous ShapeInterface example, you will need to support the new three-dimensional shapes of Cuboid and Spheroid, and these shapes will need to also calculate volume.

Let’s consider what would happen if you were to modify the ShapeInterface to add another contract:

Now, any shape you create must implement the volume method, but you know that triangles are flat shapes and that they do not have volumes, so this interface would force the Triangle class to implement a method that it has no use of.

This would violate the interface segregation principle. Instead, you could create another interface called ThreeDimensionalShapesInterface that has the volume contract and three-dimensional shapes can implement this interface:

(...)
class ShapeInterface:
    def area(self) -> float:
        pass


class ThreeDimensionalShapeInterface:
    def volume(self) -> float:
        pass


class Triangle(ShapeInterface):
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return self.base * self.height / 2


class Cuboid(ThreeDimensionalShapeInterface):
    # implementation...

(...)

This is a much better approach that satisfies the interface segregation principle.

Dependency Inversion Principle

Entities must depend on abstractions, not on concretions. It states that the high-level module must not depend on the low-level module, but they should depend on abstractions.

ROBERT C. MARTIN

This principle allows for decoupling.

Here is an example of a PasswordReminder that connects to a MySQL database:

class MySQLConnection:
    def connect(self):
        # handle the database connection
        return 'Database connection';


class PasswordReminder:
    _dbConnection;

    def __init__(dbConnection)
        self._dbConnection = dbConnection;

First, the MySQLConnection is the low-level module while the PasswordReminder is high level, but according to the definition of D in SOLID, which states to Depend on abstraction, not on concretions. This snippet above violates this principle as the PasswordReminder class is being forced to depend on the MySQLConnection class.

Later, if you were to change the database engine, you would also have to edit the PasswordReminder class, and this would violate the open-close principle.

The PasswordReminder class should not care what database your application uses. To address these issues, you can code to an interface since high-level and low-level modules should depend on abstraction:

class DBConnectionInterface:
    def connect(self) -> str:
        pass

The interface has a connect method and the MySQLConnection class implements this interface. Also, instead of directly type-hinting MySQLConnection class in the constructor of the PasswordReminder, you instead type-hint the DBConnectionInterface, and no matter the type of database your application uses, the PasswordReminder class can connect to the database without any problems and the open-closed principle is not violated.

class MySQLConnection(DBConnectionInterface):
    def connect(self):
        # handle the database connection
        return 'Database connection';


class PasswordReminder:
    _dbConnection;

    def __init__(dbConnection)
        self._dbConnection = dbConnection;

This code establishes that both the high-level and low-level modules depend on abstraction.

Conclusion

In this article, you were presented with the five principles of SOLID Code. Projects that adhere to SOLID principles can be shared with collaborators, extended, modified, tested, and refactored with fewer complications.

Apex Constants Class Benefits and Best Practices

Introduction

Every software developer who has worked on a large project knows that sooner or later it is necessary to refactor the code, either due to changes in the business logic or due to optimization.

A good programmer must not only solve the problem efficiently but also provide a solution whose code is maintainable. To achieve the latter, the best recipe is always to follow good programming practices associated with the development environment.

Salesforce, of course, is not the exception, and here we will see the use of Constants to make our code clearer and more maintainable.

Apex Constants


In Apex, Constants are class members, generally static, whose values can be initialized only once.

public class someApexClass {
   static final String DEFAULT_NAME = 'Jhon Doe';
   static final String MAX_CONNECTIONS = 5;
}

The final keyword denotes this functionality, and as a convention, the name is used in capitalized snake case.

Note that the Constants, after being initialized, do not vary during a transaction, but their values ​​can be easily modified when it is necessary to refactor the logic dependent on it.

Apex Constants Class

Using Constants allows you to change a common element to several methods of a class quickly in one place, but many times we find configurations common to several classes.

A class whose functionality is to provide Constants to other ones allows to quickly change an element common to several classes.

As an example, imagine that you are a Salesforce Developer in a company that uses a status labeled “Designed” in the Opportunity sobject. This status is used in many places in the code, and one day the Scrum Product Owner requests changing the label to “Design Ready”.

Salesforce allows you to rename the status in the sobject configuration and reassign the values ​​of the existing records, but, what about the code? If the old status label is in each different class, we must change it by hand, which forces us to deploy all those classes, and perhaps to forget something in the deployment. If we previously defined a Constant and used it in all these classes, we only need to change the value in one place.

A Word About Performance

As Salesforce Developers, you know that Sharing Is Caring in the Multitenant Cloud, so we work for efficiency.

It is not the intention of this post to explain in detail what a Property is in Apex and its functionalities, but when the Apex Constants Class grows using the syntax that we presented previously (members syntax), the execution time of all the transactions that use any of the Constants of this class grows significantly.

A class with approximately 300 declared Constants can add about a second of execution time to each transaction (Relative to the hardware, but significant).

The solution: declaring Constants as properties cause them to load as they are requested, and not all at once (lazyload).

public class someApexClass {
   static final String DEFAULT_NAME { get{ return 'Jhon Doe'; }};
   static final String MAX_CONNECTIONS { get{ return 5; }};
}

However, this makes requesting the constant more than once slower than the previous way, so if it is used in a heavy cycle, the operation can be more expensive than using members.

As a general rule, it is advisable to use properties (lazyload) when we have a lot of Constants defined in our class, and members otherwise.

How To Start WordPress with Caddy using Docker Compose

Introduction

WordPress is a popular content management system (CMS). It can be used to set up blogs and websites quickly and easily, and almost all of its administration is possible through a web interface.

In most cases, WordPress is installed using a LAMP or LEMP stack (i.e. using either Apache or Nginx as a web server). In this guide, we’ll set up WordPress with Caddy instead. Caddy is a new web server quickly gaining popularity for its wide array of unique features, like HTTP/2 support and automatic TLS encryption with Let’s Encrypt, a popular free certificate provider.

In this tutorial, you will configure Docker Compose to start WordPress with Caddy v2.

Install Docker

  1. Update the apt package index and install packages to allow apt to use a repository over HTTPS:
$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
  1. Add Docker’s official GPG key:
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Verify that you now have the key with the fingerprint 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88, by searching for the last 8 characters of the fingerprint.

$ sudo apt-key fingerprint 0EBFCD88

pub   rsa4096 2017-02-22 [SCEA]
      9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid           [ unknown] Docker Release (CE deb) <docker@docker.com>
sub   rsa4096 2017-02-22 [S]
  1. Use the following command to set up the stable repository
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
  1. Update the apt package index, and install the latest version of Docker Engine and containerd, or go to the next step to install a specific version:
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
  1. Verify that Docker Engine is installed correctly by running the hello-world image.
$ sudo docker run hello-world

This command downloads a test image and runs it in a container. When the container runs, it prints an informational message and exits.

  1. If you would like to use Docker as a non-root user, you should now consider adding your user to the “docker” group with something like:
    sudo usermod -aG docker your-user

Remember to log out and back in for this to take effect!

Install Docker-Compose

  1. Run this command to download the current stable release of Docker Compose:
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  1. Apply executable permissions to the binary:
sudo chmod +x /usr/local/bin/docker-compose
  1. Test your installation:
$ docker-compose --version
docker-compose version 1.26.2, build 1110ad01

Step 1: Create docker-compose.yml

version: '3.3'
services:
  # Database
  database:
    image: mysql:latest
    container_name: database
    volumes:
      - ./db:/var/lib/mysql
    restart: always
    env_file: 
      - .env
    environment: 
      MYSQL_DATABASE: blog_wp
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - blog-network
  # WordPress
  wordpress:
    depends_on:
      - database
    image: wordpress:php7.4-fpm-alpine
    container_name: wordpress
    restart: always
    user: "root:root"
    env_file: 
      - .env
    environment:
      - WORDPRESS_DB_HOST=database:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=blog_wp
    volumes:
      - ./php.ini:/usr/local/etc/php/conf.d/custom.ini
      - ./wordpress:/var/www/html
    networks:
      - blog-network
  # Webserver
  caddy:
    image: caddy:alpine
    container_name: webserver
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./wordpress:/var/www/html
      - ./caddy_data:/data
      - ./caddy_config:/config
      - ./Caddyfile:/etc/caddy/Caddyfile
    networks:
      - blog-network
networks:
  blog-network:
    driver: bridge

docker-compose.yml

The above docker-compose.yml requires .env file to contain password and others environment variables:

MYSQL_ROOT_PASSWORD=
MYSQL_USER=
MYSQL_PASSWORD=

.env

Create a php.ini file to allow big file update to your wordpress instance:

memory_limit = 64M
upload_max_filesize = 64M
post_max_size = 64M

php.ini

Create folder to store caddy config, database and the wordpress code for custom development:

$ mkdir caddy_data caddy_config wordpress db

Step 2: Caddyfile

example.com {
        root * /var/www/html
        php_fastcgi wordpress:9000
        file_server
}

Caddyfile

Your folder should look like this now:

Step 3: Set up WordPress

To start, let’s run docker:

$ docker-compose up -d

Open the domain in the web browser

Powered by WordPress & Theme by Anders Norén